The Datagram Transport Layer Security (DTLS) Protocol Version 1.3
obsoletes
- rfc-6347 — Datagram Transport Layer Security Version 1.2
updated by
- rfc-9853 — Return Routability Check for DTLS 1.2 and 1.3
Extracted elements (29)
EndOfEarlyData is omitted from DTLS 1.3 because epochs already demarcate the early-data/handshake-traffic boundary, making the message redundant. Additionally, DTLS's lossy nature means that deletion attacks EndOfEarlyData prevents in TLS are trivially achievable in DTLS regardless.
The DTLSCiphertext unified header fixes its top 3 bits to '001', placing encrypted records in the range 32–63, which is disjoint from all prior DTLS plaintext content types. This enables demultiplexing by inspecting only the first byte, and allows DTLS 1.2 and 1.3 records to coexist on the same host/port quartet when CIDs are in use.
The HKDF label 'dtls13' omits a trailing space (unlike TLS 1.3's 'tls13 ') to keep the overall label within one hash iteration, since 'DTLS' is one character longer than 'TLS' and adding a space would exceed a single iteration boundary.
DTLS 1.3 replaces the DTLS 1.2 HelloVerifyRequest with the TLS 1.3 HelloRetryRequest plus 'cookie' extension; DTLS 1.3-only implementations MUST NOT send HelloVerifyRequest. Dual-stack DTLS 1.2/1.3 clients MUST accept HelloVerifyRequest from DTLS 1.2 servers. DTLS 1.3 servers ignore the legacy_cookie field in ClientHello.
A DTLS 1.3 client MUST set the legacy_cookie field to zero length in ClientHello. If a server receives a DTLS 1.3 ClientHello with a non-zero legacy_cookie value, the server MUST abort the handshake with an 'illegal_parameter' alert.
Any TLS cipher suite specified for use with DTLS MUST define limits on AEAD usage preserving margins for both confidentiality (authenticated-packet count) and integrity (failed-authentication count), and MUST provide a reference to supporting analysis including all assumptions.
DTLS 1.3 does not use TLS 1.3 compatibility mode: servers MUST NOT echo the legacy_session_id from the client, and endpoints MUST NOT send ChangeCipherSpec messages.
For DTLS over TCP or SCTP, the upper layer MUST NOT write any record exceeding 2^14 bytes. For DTLS over UDP or DCCP, PMTU estimates SHOULD be made available to upper layer protocols; ICMP PMTU indications below IPv4/IPv6 minimums (576/1280 bytes) SHOULD be ignored.
Implementations MUST count received packets failing authentication per key. If failures exceed the AEAD-specific integrity limit—2^36 for AEAD_AES_128_GCM, AEAD_AES_256_GCM, and AEAD_CHACHA20_POLY1305; 2^23.5 for AEAD_AES_128_CCM—the connection SHOULD be closed immediately. Implementations SHOULD initiate a key update before reaching the limit.
Implementations MUST either discard or buffer all application data records for epoch 3 and above until the Finished message from the peer has been received. Retransmissions of lost handshake messages MUST use the same epoch and keying material as the original transmission.
Implementations MUST NOT allow the epoch counter to wrap; a new association MUST be established and the old one terminated. Implementations MUST also either abandon the association or rekey before the per-epoch sequence number wraps.
Invalid DTLS records (bad formatting, length, or authentication) SHOULD be silently discarded to preserve the association. Implementations that generate alerts MUST use fatal alerts. Generating fatal alerts is NOT RECOMMENDED over UDP because UDP forgery enables DoS attacks and spoofed traffic to third parties.
The DTLS 1.3 HKDF label prefix SHALL be 'dtls13' (no trailing space), replacing TLS 1.3's 'tls13 ', ensuring cryptographic key separation between DTLS 1.3 and TLS 1.3 key derivation.
The retransmission timer SHOULD use an initial value of 1000 ms, doubling at each retransmission up to at least 60 seconds maximum. When external RTT information is available (e.g., from ICE), implementations SHOULD use 1.5× the RTT estimate as the initial value. For DTLS-SRTP, a default of 400 ms is RECOMMENDED.
TLS_AES_128_CCM_8_SHA256 MUST NOT be used in DTLS without additional safeguards against forgery, because AEAD_AES_128_CCM_8 has no single authentication-failure limit that both bounds forgery probability and avoids denial-of-service risk simultaneously.
When a server with an existing association receives an epoch=0 ClientHello from the same address, it SHOULD proceed with a new handshake but MUST NOT destroy the existing association until the client demonstrates reachability via cookie exchange or a verifiable Finished. After a correct Finished is received, the server MUST abandon the previous association.
Connection IDs appear in cleartext in every DTLSCiphertext record and can be used to correlate traffic across network path changes (e.g., NAT rebinding), enabling device tracking across network transitions. Implementations should weigh this privacy cost against the operational benefits of CIDs.
Anti-replay uses a per-epoch sliding window adapted from RFC 4303 §3.4.3. Each epoch has its own counter initialized to zero; records outside the window or duplicates are rejected. The window MUST NOT be updated until a record is successfully deprotected, to avoid timing side-channels on the record number.
Epoch values are fixed per protocol phase: 0 for unencrypted messages (ClientHello, ServerHello, HelloRetryRequest), 1 for early data (client_early_traffic_secret), 2 for handshake traffic, 3 for initial application traffic (application_traffic_secret_0), and 4+ for subsequent rekeyed application traffic after KeyUpdate.
Record sequence numbers in DTLSCiphertext are encrypted using a mask derived from ciphertext bytes: AES-ECB(sn_key, Ciphertext[0..15]) for AES-based AEADs, or ChaCha20(sn_key, Ciphertext[0..3], Ciphertext[4..15]) for ChaCha20. The per-epoch sn_key is derived via HKDF-Expand-Label(Secret, "sn", "", key_length).
Sequence number reconstruction: when the abbreviated epoch bits in a received record match the current epoch, implementations SHOULD reconstruct the full sequence number as the value numerically closest to one plus the highest successfully deprotected sequence number. After the handshake, mismatched epoch bits trigger use of the most recent past epoch with matching bits.
The ACK message (content type 26) contains a list of RecordNumber values (each 128 bits: uint64 epoch + uint64 sequence_number) identifying received records. It enables selective retransmission during handshake and confirms receipt of final-flight messages that would otherwise have no protocol response.
IANA must reserve ContentType range 32–63 (0x20–0x3F) to avoid demultiplexing conflicts with DTLS 1.3 encrypted records. Content type 26 is allocated for the new ACK message. Handshake message types 9 (request_connection_id) and 10 (new_connection_id) are registered for CID lifecycle management.
Record number encryption hides per-record sequence numbers from on-path observers, preventing traffic-analysis attacks based on sequence number patterns. Encrypted records MUST be at least 16 bytes for the encryption mechanism to function; receivers MUST reject shorter records as deprotection failures.
Unlike TLS, which closes on any authentication failure, DTLS silently discards unauthenticated packets, enabling an attacker to make repeated forgery attempts indefinitely. Per-key authentication-failure counters with AEAD-specific limits are therefore essential to bound attacker advantage in the DTLS threat model.
DTLS uses a four-state retransmission machine: PREPARING (compute next flight, buffer messages), SENDING (transmit buffered flight, arm retransmit timer), WAITING (await ACK, timer expiry, or incoming flight), and FINISHED. Clients start in PREPARING; servers start in WAITING with empty buffers. In FINISHED, the server MUST respond to the client's final-flight retransmissions with ACK for at least 2×MSL.
DTLSHandshake adds three fields to the TLS Handshake structure: uint16 message_seq (ordering), uint24 fragment_offset, and uint24 fragment_length (fragmentation support). These three fields are excluded from the transcript hash computation in DTLS 1.3, a change from DTLS 1.2.
DTLSPlaintext carries unprotected records: ContentType (1 byte), legacy_record_version (2 bytes, must be {254,253}), epoch (2 bytes, always 0), sequence_number (uint48, 6 bytes), length (2 bytes), and opaque fragment. DTLSCiphertext uses a variable-length unified_hdr followed by an encrypted_record.
The DTLSCiphertext unified header begins with a fixed-bits byte (top 3 bits = 001, plus C/S/L/E flag bits), followed by an optional variable-length Connection ID, an 8- or 16-bit sequence number (S bit selects width), and an optional 16-bit length field (L bit). The low 2 bits (E) carry the low-order epoch bits.