ossl-guide-dtlsv13¶
NAME¶
ossl-guide-dtlsv13 - OpenSSL DTLSv1.3
INTRODUCTION¶
This page provides an introduction to the DTLSv1.3 protocol. It explains the concepts behind the protocol and how it differs from previous versions of DTLS.
DESCRIPTION¶
DTLSv1.3 is a version of the Datagram Transport Layer Security (DTLS) protocol. DTLSv1.3 is defined in RFC 9147. It is based on TLSv1.3 and provides similar security guarantees to TLSv1.3 but is designed to work over datagram transport protocols such as UDP.
The Supported Versions extension in the ClientHello and ServerHello is updated to include support for DTLSv1.3 by adding the value 0xFEFD to the list of supported versions.
The TLSv1.3 state machine has been updated to support the DTLSv1.3 protocol. The main difference in the state machine between TLSv1.3 and DTLSv1.3 is the addition of Acknowledgement messages that are used to acknowledge the receipt of messages in the DTLSv1.3 protocol. This allows either the client or the server to signal to the peer that they have received and processed a handshake message. This prevents unnecessary retransmissions of messages.
A major difference between DTLSv1.3 and DTLSv1.2 is the addition of the Unified Header. This Unified Header is in plaintext, and it contains an encrypted sequence number. The handshake header follows the Unified Header, and both the handshake header and all of its contents are encrypted.
struct {
ContentType type;
ProtocolVersion legacy_record_version;
uint16 epoch_val = 0;
uint48 sequence_number;
uint16 length;
opaque fragment[DTLSPlaintext.length];
} DTLSPlaintext;
struct {
opaque content[DTLSPlaintext.length];
ContentType type;
uint8 zeros[length_of_padding];
} DTLSInnerPlaintext;
struct {
opaque unified_hdr[variable];
opaque encrypted_record[length];
} DTLSCiphertext;
UNIFIED HEADER¶
The first byte of a Unified Header indicates which fields are present in the Unified Header.
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|0|0|1|C|S|L|E E|
+-+-+-+-+-+-+-+-+
| Connection ID | Legend:
| (if any, |
/ length as / C - Connection ID (CID) present
| negotiated) | S - Sequence number length
+-+-+-+-+-+-+-+-+ L - Length present
| 8 or 16 bit | E - Epoch
|Sequence Number|
+-+-+-+-+-+-+-+-+
| 16 bit Length |
| (if present) |
+-+-+-+-+-+-+-+-+
When OpenSSL is generating a Unified Header it will always have Connection ID (C) set to zero. The Sequence Number will always be set to 1, indicating a 2 byte Sequence Number. Lastly the length bit will always be set indicating a length is present in the Unified Header.
Epoch¶
The Epoch value is incremented every time the cryptographic parameters change. ClientHello and ServerHello use an Epoch of 0. Only the client uses an Epoch of 1 when it is sending Early Data. The rest of the handshake messages use an Epoch of 2.
Encrypted Sequence Numbers¶
The Sequence Number in the Unified Header is encrypted. For each Epoch, it always starts at 0, but since it is encrypted, it will not appear as zero when viewing it on the wire. The Sequence Number increments with every new DTLSv1.3 record.
ACKNOWLEDGEMENT MESSAGE¶
DTLSv1.3 adds explicit Acknowledgement messages to the protocol. There are also implicit acknowledgements in DTLSv1.3. For instance, when the client receives ServerHello, that ServerHello implicitly acknowledges the receipt of the ClientHello message. Also, ClientFinished implicitly acknowledges the receipt of ServerFinished. The new explicit Acknowledgement messages are used for the server to acknowledge the receipt of the ClientFinished message. The client uses an explicit Acknowledgement message to acknowledge the receipt of NewSessionTicket messages. Both the server and the client use the explicit Acknowledgement to acknowledge the receipt of KeyUpdate messages.
When the server sends data immediately after the connection is established, there is a potential race condition where the Application Data may be received by the client before the Acknowledgement message for the ClientFinished message is received. OpenSSL buffers the Application Data until the Acknowledgement message is received and processed.
END OF EARLY DATA MESSAGE¶
In DTLSv1.3, the End of Early Data message is not sent. Instead, when the client starts sending messages in Epoch 2, this signals to the server that the client has finished sending Early Data.
HELLO RETRY REQUEST¶
For DTLSv1.3 server applications, use SSL_new_listener(3): HelloRetryRequest cookie validation is enabled by default. This provides connection demultiplexing for multiple clients on a single UDP socket.
Note that DTLSv1_listen(3) only supports DTLS 1.0/1.2 with HelloVerifyRequest and cannot be used with DTLSv1.3.
DTLS LISTENER API¶
For DTLSv1.3 server applications that need to handle multiple clients on a single UDP socket, the SSL Listener API provides connection demultiplexing and address validation. Unlike DTLSv1_listen(3) which only supports DTLS 1.0/1.2 with HelloVerifyRequest, the listener API fully supports DTLSv1.3 with HelloRetryRequest cookie validation.
Create a DTLS listener using SSL_new_listener(3) with an SSL_CTX configured for DTLS. Address validation is enabled by default. The following flags adjust listener behavior:
SSL_LISTENER_FLAG_ADDRESS_VALIDATION
Requests address validation by HelloRetryRequest and HelloVerifyRequest. This is the default.
SSL_LISTENER_FLAG_NO_VALIDATE
Disables all address validation. The listener will not send HelloVerifyRequest (for DTLS 1.0/1.2) or HelloRetryRequest with cookie (for DTLSv1.3). This is faster but provides no protection against amplification attacks. Not recommended for use in untrusted network environments. If both this flag and SSL_LISTENER_FLAG_ADDRESS_VALIDATION are specified, the listener fails safe and performs address validation.
SSL_LISTENER_FLAG_SINGLE_THREAD
Specifies that the DTLS listener will operate in single-threaded mode. When this flag is set, the listener and all connections accepted from it should only be used from a single thread. This avoids the overhead of internal synchronization mechanisms.
By default (when SSL_LISTENER_FLAG_SINGLE_THREAD is not set), the listener initializes internal synchronization mechanisms that allow connections accepted from the listener to be safely used from multiple threads concurrently. This includes a notifier mechanism that enables efficient polling across threads.
After attaching a UDP socket BIO with SSL_set0_rbio(3) and SSL_set0_wbio(3) and calling SSL_listen(3), use SSL_accept_connection(3) to accept incoming connections. Each accepted connection has completed cookie validation (if required) but still requires SSL_do_handshake(3) or SSL_accept(3) to complete the TLS handshake before application data can be exchanged.
By default a DTLS listener created with SSL_new_listener(3) operates in blocking mode. Because a listener demultiplexes a single UDP socket across many connections, it cannot allow a read for one connection to block and thereby stall the others. Its network BIO is therefore configured for nonblocking operation, and blocking is instead provided by waiting for readiness of the underlying socket. This applies to SSL_accept_connection(3) on the listener as well as to SSL_read(3) and SSL_write(3) on the connections it returns.
Use SSL_set_blocking_mode(3) to enable or disable blocking mode, and SSL_get_blocking_mode(3) to query it. The blocking mode of a listener is normally configured once, before it is used.
A connection SSL object returned by SSL_accept_connection(3) inherits the blocking mode of the listener it came from. Calling SSL_set_blocking_mode(3) on such a connection overrides that inheritance for that connection only. To perform a single nonblocking accept on an otherwise blocking listener, pass the SSL_ACCEPT_CONNECTION_NO_BLOCK flag to SSL_accept_connection(3).
See SSL_set_blocking_mode(3) for full details.
The following tunables are available via SSL_get_value_uint(3) / SSL_set_value_uint(3): SSL_VALUE_DTLS_LISTENER_MAX_PENDING_CONNS (pending-connection cap, default 256), SSL_VALUE_DTLS_LISTENER_PENDING_TIMEOUT (pending connection timeout in milliseconds, default 30000; pending connections that do not complete their handshake within this period are automatically cleaned up), and SSL_VALUE_DTLS_LISTENER_MAX_DGRAM_SIZE (maximum received datagram size in bytes, default 2000).
See SSL_new_listener(3) for complete API documentation.
The complete source code for an example DTLS listener server (and a matching client) is available in the demos/dtlslistenerecho directory of the OpenSSL source distribution in the file main.c. It is also available online at https://github.com/openssl/openssl/blob/master/demos/dtlslistenerecho/main.c.
APPLICATION DATA¶
When either the DTLSv1.3 client or server is awaiting an Acknowledgement message, Application Data will be buffered until the Acknowledgement message is received and processed. Once either the DTLSv1.3 client or server processes the Acknowledgement message it will then process the buffered Application Data.
Another scenario when Application Data is buffered is when multiple handshake messages like the NewSessionTicket are received with Application Data. The Application Data will be buffered until all the handshake messages have been processed.
SCTP¶
Even though DTLSv1.3 is enabled, the client will only perform a DTLSv1.2 or DTLSv1.0 handshake. If a DTLSv1.3 over SCTP compatible client connects to an OpenSSL server, the server will downgrade to DTLSv1.2 or earlier.
CONNECTION ID¶
OpenSSL does not support Connection IDs in DTLSv1.3. If a client that supports Connection IDs connects to an OpenSSL server, the server will ignore the Connection ID and will not include a Connection ID in its responses.
HOW TO UTILIZE DTLSV1.3 IN OPENSSL¶
SSL_set_min_proto_version and SSL_set_max_proto_version¶
Use SSL_set_min_proto_version(3) and SSL_set_max_proto_version(3) with the DTLS1_3_VERSION macro to set the minimum and maximum protocol version to DTLSv1.3.
SSL_CTX_set_min_proto_version and SSL_CTX_set_max_proto_version¶
Use SSL_CTX_set_min_proto_version(3) and SSL_CTX_set_max_proto_version(3) with the DTLS1_3_VERSION macro to set the minimum and maximum protocol version to DTLSv1.3.
s_client¶
Use the min_protocol and max_protocol parameters and set them to DTLSv1.3. Use the -dtls option to specify that you want to use DTLS instead of TLS.
s_server¶
Use the min_protocol and max_protocol parameters and set them to DTLSv1.3.
DEMOS¶
OpenSSL is distributed with two DTLS demo applications that illustrate the concepts described on this page. They can be found in the demos directory of the OpenSSL source distribution:
demos/dtlsecho
A simple DTLSv1.3 echo client and server built on a single SSL object per connection. The complete source code is available in the demos/dtlsecho directory of the OpenSSL source distribution in the file main.c. It is also available online at https://github.com/openssl/openssl/blob/master/demos/dtlsecho/main.c.
demos/dtlslistenerecho
A DTLS echo server that uses the DTLS listener API (SSL_new_listener(3), SSL_listen(3) and SSL_accept_connection(3)) to demultiplex and handle multiple client connections on a single UDP socket, together with a matching client. The complete source code is available in the demos/dtlslistenerecho directory of the OpenSSL source distribution in the file main.c. It is also available online at https://github.com/openssl/openssl/blob/master/demos/dtlslistenerecho/main.c.
SEE ALSO¶
ossl-guide-introduction(7), ossl-guide-libraries-introduction(7), ossl-guide-libssl-introduction(7), ossl-guide-tls-introduction(7), SSL_set_blocking_mode(3), SSL_new_listener(3), SSL_accept_connection(3)
COPYRIGHT¶
Copyright 2026 The OpenSSL Project Authors. All Rights Reserved.
Licensed under the Apache License 2.0 (the "License"). You may not use this file except in compliance with the License. You can obtain a copy in the file LICENSE in the source distribution or at https://www.openssl.org/source/license.html.