Skip to main content
Version: Next

DTLS

Algorithm

DTLS (Datagram TLS) reuses TLS cipher suites and handshake transcripts over UDP (or similar datagram transports). Compared to stream TLS it adds:

  • Explicit record framing so each datagram can be parsed independently (13-byte record headers in this implementation).
  • Epoch and sequence numbers per record so keys can rotate and replays can be rejected within an epoch.
  • Handshake fragmentation (DTLS 1.2 style) so large Certificate / CertificateVerify chains fit under the path MTU.
  • Retransmission and flight logic at the application or connection layer, because datagrams are not reliably delivered.

NoxTLS implements DTLS-oriented record encoding/decoding, DTLS 1.2 handshake fragment helpers, replay windows, flight retransmit tracking, DTLS 1.3 RFC-core unified-header record seal/open helpers, ACK range handling, and Connection datagram-driver APIs for DTLS 1.3 application data, ACK processing, and retransmit polling. Wire compatibility against external peers should still be validated for your target profile (see TLS).

Purpose

Use these APIs when you integrate noxtls on a datagram transport: build or parse DTLS records, bound handshake reassembly, apply anti-replay checks, size AEAD packets, and tune retransmit / flight timeouts on a Connection configured for TlsVersion::Dtls12 or TlsVersion::Dtls13.

Rust API

  • Crate: noxtls (types and functions are re-exported from the crate root).
  • Module path (implementation): noxtls::protocol (see protocol/dtls.rs and DTLS fields on Connection in protocol/connection.rs).

Record layer (DTLS 1.2 / 1.3 framing)

SymbolRole
DtlsRecordHeaderParsed or built header: content_type, version ([u8; 2], e.g. DTLS 1.2 0xFEFD, DTLS 1.3 0xFEFC), epoch, sequence (48-bit, must be ≤ (1<<48)-1), length (payload length).
noxtls_encode_dtls_record_header(header) -> Result<[u8; 13]>Serializes one header; errors if sequence is out of range.
noxtls_parse_dtls_record_header(input) -> Result<(DtlsRecordHeader, &[u8])>Requires at least 13 bytes; rejects unknown RecordContentType.
noxtls_encode_dtls_record_packet(content_type, version, epoch, sequence, payload) -> Result<Vec<u8>>header || payload; payload length must fit in u16 (same on-the-wire limit as TLS record length).
noxtls_parse_dtls_record_packet(input) -> Result<(DtlsRecordHeader, Vec<u8>)>Parses header then checks body.len() == header.length (mitigates length-desync attacks).

DTLS 1.2 handshake fragmentation

SymbolRole
DtlsHandshakeFragmentStructured view of one fragment: handshake_type, 24-bit message_len, message_seq, fragment_offset, fragment_len, fragment_body.
noxtls_encode_dtls12_handshake_fragments(handshake_type, message_seq, body, max_fragment_len) -> Result<Vec<Vec<u8>>>Splits a full handshake body into ordered fragment records (each 12-byte fragment header + body). max_fragment_len must be > 0; total message length must fit in 24 bits.
noxtls_parse_dtls12_handshake_fragment(input) -> Result<DtlsHandshakeFragment>Validates header/body consistency and that offset + len ≤ message_len.
noxtls_reassemble_dtls12_handshake_fragments(fragments, max_message_len) -> Result<(u8, u16, Vec<u8>)>Verifies all fragments share type, message_seq, and message_len, allocates message_len bytes, copies ranges, and requires every byte filled (gap/overlap detection via a filled bitmap). max_message_len caps allocation (anti-amplification style bound).

DTLS 1.3 RFC-core records and ACKs

SymbolRole
Dtls13RecordHeaderRFC-core unified-header view without Connection ID: low epoch bits, 1- or 2-byte sequence number, and explicit payload length.
noxtls_encode_dtls13_record_header / noxtls_parse_dtls13_record_headerEncode/decode DTLS 1.3 unified headers. CID-bearing headers are rejected with an explicit unsupported error in the RFC-core profile.
noxtls_seal_dtls13_unified_aes128gcm_record / noxtls_open_dtls13_unified_aes128gcm_recordSeal/open DTLS 1.3 unified-header AES-128-GCM records with replay checks and header AAD.
Dtls13AckRange, noxtls_encode_dtls13_ack, noxtls_parse_dtls13_ack, noxtls_apply_dtls13_ack_rangesEncode/decode ACK ranges and apply ACKs to retransmit tracking.
Legacy helpersnoxtls_seal_dtls13_aes128gcm_record and noxtls_open_dtls13_aes128gcm_record remain for compatibility with existing 13-byte DTLS-style harnesses; new endpoint code should prefer the unified helpers.

DTLS 1.3 Connection datagram driver

SymbolRole
process_dtls13_datagram(input, now_ms)Opens one inbound DTLS 1.3 datagram and emits Dtls13TransportEvent::{Handshake,Ack,Alert,ApplicationData,DatagramIgnored}.
poll_dtls13(now_ms)Emits generated ACK packets and due active-flight retransmits.
write_dtls13_application_data(plaintext, now_ms)Seals application data after HandshakeState::Finished.
read_dtls13_application_data(datagram, now_ms)Processes a datagram and returns only application-data plaintext records.

Replay and retransmit primitives

SymbolRole
DtlsReplayWindow64-sequence sliding bitmap vs latest_sequence (check_and_mark returns false on replay or stale sequences). snapshot / restore_from_snapshot for persistence.
DtlsEpochReplayTrackerTracks current and previous epoch windows; check_and_mark(epoch, sequence) accepts replays only in allowed epochs.
DtlsFlightRetransmitTrackerStores outbound packets by (epoch, sequence) with bounded history (default cap 256 in Connection), ACK marking, exponential backoff on collect_due_retransmit_packets, and pruning.
DtlsFlightRecordOne tracked outbound datagram and its retransmit schedule.

Connection policy (timers)

SymbolRole
DtlsOperationalPolicyretransmit_initial_timeout_ms, max_retransmit_attempts, active_flight_timeout_ms. Zeros are clamped to 1 when applied.
DtlsOperationalProfileConservative, LanLowLatency, LossyNetwork — preset triples mapped through set_dtls_operational_policy.
Connection::set_dtls_operational_policy / apply_dtls_operational_profileRequire version.is_dtls() (internal gate is named ensure_dtls12_mode but applies to any DTLS profile; StateError if the connection is stream TLS). active_flight_timeout_ms is also used for DTLS 1.3 active-flight timers.
Connection::dtls_operational_policyReturns Some(policy) only when version.is_dtls(); None for stream TLS.

Feature flags and policy

  • noxtls-core: feature-dtls must be enabled for DTLS profile types in core configs. It is included in profile-default and profile-tls-server-pki, but not in profile-minimal-tls-client or profile-crypto-only (see Build configuration). feature-dtls requires feature-tls (enforced at compile time in noxtls-core).
  • noxtls: Enable the noxtls-core profile that includes DTLS if you strip default features; otherwise DTLS-specific TlsVersion paths may be unavailable.

Examples

use noxtls::{noxtls_encode_dtls_record_packet, noxtls_parse_dtls_record_packet, RecordContentType};

// DTLS 1.2 record version bytes (example only).
let packet = noxtls_encode_dtls_record_packet(
RecordContentType::ApplicationData,
[0xfe, 0xfd],
1,
42,
b"hello",
)
.unwrap();
let (_header, payload) = noxtls_parse_dtls_record_packet(&packet).unwrap();
assert_eq!(payload, b"hello");

See also repository examples/dtls_client.rs and examples/dtls_server.rs for end-to-end usage patterns.

Security and compatibility

  • Length checks: Always use noxtls_parse_dtls_record_packet (or header parse + explicit bound) so advertised length cannot drive unbounded reads off one UDP payload.
  • Handshake reassembly: Pass a max_message_len aligned with your MTU / amplification policy; noxtls_reassemble_dtls12_handshake_fragments rejects oversized message_len and incomplete coverage.
  • Replay: Use DtlsEpochReplayTracker (or equivalent) for inbound records per epoch; datagram duplication is common on the public Internet.
  • Retransmit: DtlsFlightRetransmitTracker caps stored flights; tune max_records and max_retransmit_attempts so memory and retry storms stay bounded.
  • AEAD: DTLS 1.3 record helpers assume AES-128-GCM with the 12-byte static IV layout used in the implementation; confirm nonce/AAD rules match your interoperability profile.