UDP Security
On this page
This guide explains the actual UDP session shape used by UdpListenerBase today, in a client-friendly way.
Use it when you already know you need UDP and want to understand the trust and replay rules before implementing a client.
What the runtime expects
When UdpListenerBase receives a datagram, it expects a 12-byte authentication wrapper around the serialized Nalix packet (minimum datagram size is 18 bytes = SessionTokenSize (8) + PacketHeader.Size (6) + UdpMacTagSize (4)):
- session token (
Snowflake, 8 bytes at[0..8]) - payload (at least the 6-byte Nalix
PacketHeaderat[8..^4]:[OpCode(2), Flags(1), Priority(1), SequenceId(2)]) XxHash32authentication tag (4 bytes at[^4..^0])
In source (UdpListener.Receive.cs), the listener validates:
- the datagram is at least
18bytes long (8token +6PacketHeader.Size+4XxHash32tag) - the 8-byte token resolves to an active, non-disposed connection in
ConnectionHubwith a non-zeroSecret - the trailing 4-byte
XxHash32tag matchesXxHash32.Compute(connection.Secret.AsSpan(), seed: XxHash32.Compute(datagram[..^4])) - the remote endpoint matches the connection’s pinned endpoint (
IsPinnedEndpointMatch) - the UDP replay window (
connection.UdpReplayWindow.TryCheck(header.SequenceId)) accepts the packet sequence - your overridden
IsAuthenticated(...)or hosting predicate returnstrue
High-level flow
sequenceDiagram
participant Client
participant TCP as TcpListenerBase
participant Hub as ConnectionHub
participant UDP as UdpListenerBase
Client->>TCP: 1. Connect & HandshakeAsync()
TCP->>Hub: 2. Store connection (SessionToken + 32B Secret)
TCP-->>Client: 3. SessionEstablished (8B SessionToken)
Client->>UDP: 4. Datagram [8B Token][Payload][4B XxHash32 MAC]
UDP->>Hub: 5. Resolve session by 8B Token & verify 4B XxHash32 MAC
UDP->>UDP: 6. Replay check + IsAuthenticated(...)Server shape
1. Subclass UdpListenerBase
public sealed class SampleUdpListener : UdpListenerBase
{
public SampleUdpListener(IProtocol protocol, IConnectionHub hub) : base(protocol, hub) { }
public override bool IsAuthenticated(IConnection connection, EndPoint remoteEndPoint, ReadOnlySpan<byte> payload)
{
// Add your own checks here:
// - allowed endpoint
// - session state
// - region / shard ownership
return !connection.Secret.IsZero;
}
}
2. Keep TCP and UDP tied to the same session
The UDP path assumes there is already a known connection in ConnectionHub.
That means a common pattern is:
- authenticate or handshake on TCP first
- create/populate the connection secret
- store the session in
ConnectionHub - allow UDP packets to reference that session ID
Conceptual client packet layout
The listener validates these three parts:
[8-byte session-token][payload (>= 6B PacketHeader)][4-byte XxHash32 MAC]
The token is the Snowflake session ID assigned to the connection after TCP handshake, and the trailing 4-byte tag is computed over [session-token][payload] seeded with the 32-byte connection.Secret.
Pseudocode for client-side send
// Using Nalix.SDK UdpSession handles this automatically:
await using var udp = new UdpSession(udpOptions, tcpSession.State);
await udp.ConnectAsync(host, port);
await udp.SendAsync(packet);
// Or manually constructing the wire datagram:
uint bodyHash = XxHash32.Compute(tokenAndPayload, seed: 0);
uint macTag = XxHash32.Compute(sharedSecret.AsSpan(), seed: bodyHash);
Transport rules
The runtime uses an 8-byte SessionToken prefix and a trailing 4-byte XxHash32 authentication tag keyed by the 32-byte X25519 session secret established through TCP.
For clients, that means:
- send the 8-byte session token first and append the 4-byte
XxHash32tag last (or useUdpSessionwhich does both automatically) - keep the total datagram small enough to fit
MaxUdpDatagramSize(1440bytes default) - treat UDP as a fast datagram path bound to an established TCP session, not a standalone authentication protocol
What happens on failure
The listener records diagnostics for:
- short packets
- unknown sessions
- unauthenticated packets
- receive errors
Invalid packets are dropped before they reach your protocol logic.
Recommended rollout
For a simple deployment:
- establish session over TCP
- assign/store session secret
- return session ID to the client
- let the client send UDP datagrams prefixed with the session token
- verify in
IsAuthenticated(...)that the session is ready for UDP