Skip to main content

FigNet Secure Session

This page explains the current secure datagram layer used by FigNet.

Related pages:

What It Solves

Plain UDP-style networking has two practical problems:

  1. Sensitive bootstrap messages can be sent before any secure channel exists.
  2. Packet bytes can reach parsers and serializers before authenticity is known.

FigNet solves that for datagram transports by adding:

  • connection-scoped key agreement
  • authenticated encryption for protected payloads
  • packet authentication before payload deserialization

The result is:

  • RegisterIdentity and ValidateIdentity can be protected on datagram transports
  • tampered encrypted packets are dropped before higher-level parsing
  • encrypted means authenticated AEAD payload protection, not obfuscation

Transport Split

Transport kindProvidersSecurity model
DatagramENet, LiteNetLibApp-layer secure session
StreamTCP, WebSocket, WebSocketCore, WebSocketNativeTransport security only (TLS/WSS)

Cryptographic Stack

PurposeAlgorithm
Key agreementECDiffieHellman on nistP256
Key derivationHKDF-SHA256
Packet encryptionChaCha20-Poly1305
Trust modelNo pinned-key validation in the active runtime

Key properties:

  • the server creates one global keypair per process lifetime
  • the keypair is kept in memory only
  • the client creates an ephemeral keypair per connection
  • each direction gets separate derived key material

Current Identity Model

The active runtime does not pin or persist the server public key.

Current behavior:

  • when the server process first needs secure datagram support, it generates one in-memory keypair
  • that keypair is shared by all secure datagram listeners in the same process
  • when the server restarts, a new keypair is generated
  • clients do not compare the server public key against config

This keeps setup friction low and still protects the bootstrap payloads with authenticated encryption after the handshake.

What this does not provide:

  • stable server identity across restarts
  • resistance to a man-in-the-middle presenting another valid handshake key

For a stronger future model, see Secure Bootstrap (HTTPS).

Handshake Overview

The secure session is created lazily. It begins when the client first needs to send an encrypted datagram message.

Client connects transport
|
v
Needs encrypted datagram?
|
+-- no --> send plaintext normally
|
+-- yes -->
create secure session context
send SecureClientHello
wait for SecureServerHello
derive shared keys
send SecureClientFinish
mark session established
flush queued encrypted messages

Handshake Flow

Client Server
| |
|---- SecureClientHello ------------------>|
| client ephemeral public key |
| |
|<--- SecureServerHello -------------------|
| server public key + transcript proof |
| |
| derive shared secret |
| derive session keys |
| |
|---- SecureClientFinish ----------------->|
| |
| secure session established | secure session established
|---- RegisterIdentity (encrypted) ------->|
|---- ValidateIdentity (encrypted) ------->|

Handshake Messages

The secure-session control frames are reserved built-ins and always remain plaintext:

MessagePurpose
SecureClientHelloStarts the handshake and carries the client ephemeral public key
SecureServerHelloReturns the server public key and transcript proof
SecureClientFinishConfirms the client accepted the secure session
SecureHandshakeErrorSignals handshake failure

Key Derivation

Both sides use ECDH to derive the same shared secret. That secret is expanded with HKDF into:

  • client-to-server encryption key
  • server-to-client encryption key
  • client-to-server nonce prefix
  • server-to-client nonce prefix
  • handshake confirmation material

Encrypted Packet Format

Encrypted datagram packets use this wire format:

[2 bytes messageId]
[1 byte callbackId + encrypted bit]
[4 bytes sequence]
[ciphertext]
[16 bytes Poly1305 tag]

Notes:

  • the encrypted bit means app-layer encrypted datagram payload
  • the sequence is required for nonce uniqueness
  • the header plus sequence are authenticated as AAD
  • encrypted packets do not use packet magic

Plaintext Datagram Format

Plaintext datagram packets include packet magic automatically:

[2 bytes packet magic 0xC0DE]
[2 bytes messageId]
[1 byte callbackId + encrypted bit = 0]
[payload]

Current rule set:

  • plaintext datagram => packet magic included
  • encrypted datagram => packet magic omitted
  • stream transport => packet magic omitted

Packet magic is a protocol marker, not a security boundary.

Send Path

Create Message
->
Check transport kind
->
If stream:
send bytes unchanged
rely on TLS/WSS if enabled
If datagram and plaintext:
add packet magic
send
If datagram and encrypted:
if secure session not established:
queue message
start handshake
else:
write header
write sequence
encrypt payload into rented send buffer
append tag
send

Receive Path

Packet arrives
->
Resolve header offset
->
Read messageId and flags
->
If encrypted:
read sequence
authenticate + decrypt
if auth fails: drop packet
else: dispatch owned Message
If plaintext:
parse after packet-magic offset
dispatch normally

Performance Characteristics

The secure layer is designed for real-time networking:

  • no per-packet heap allocations in the hot path
  • per-connection crypto context is reused
  • send buffers are rented and written directly
  • receive payloads are decrypted before owned Message construction
  • handshake state is per connection, not global

Built-In Message Policy

For datagram transports:

Message typeEncrypted by default
RegisterIdentityYes
ValidateIdentityYes
PingNo
Secure-session handshake control messagesNo

Why XOR Was Removed

Older payload-obfuscation approaches such as XOR were removed because they do not provide real security:

  • easy to reverse
  • no authenticity
  • no tamper detection
  • misleading encrypted semantics

The active runtime keeps only:

  • ChaCha20-Poly1305 for app-layer encrypted datagrams
  • TLS/WSS for secure stream transports

Porting Checklist

If you want to implement FigNet-compatible secure datagram support in another language, the minimum requirements are:

  1. Implement the secure-session control message exchange.
  2. Support P-256 ECDH.
  3. Support HKDF-SHA256.
  4. Support ChaCha20-Poly1305.
  5. Use the exact encrypted packet structure:
    • messageId
    • flags byte
    • 4-byte sequence
    • ciphertext
    • 16-byte tag
  6. Authenticate header + sequence as AAD.
  7. Use separate key material per direction.
  8. Do not apply packet magic to encrypted datagrams.
  9. Do not implement XOR compatibility.

Source Of Truth

Active code paths:

  • FigNet/Core/SecureSessionContext.cs
  • FigNet/Core/SecureSessionProtocol.cs
  • FigNet/Core/SecureServerCredentials.cs
  • FigNet/Core/SecureSessionTransportPolicy.cs
  • FigNet/Core/MessageHeader.cs
  • FigNet/Core/ReservedMessageId.cs
  • ENetProvider/ClientSocketENet.cs
  • ENetProvider/ServerSocketEnet.cs
  • LiteNetLibProvider/ClientSocketLiteNetLib.cs
  • LiteNetLibProvider/ServerSocketLiteNetLib.cs
  • Unity mirrors under FigNetUnity/FigNet_Unity/Assets/FigNetCore/...