Table of Contents

Class ConnectionEncryptionData

Namespace
FishMMO.Auth.Implementation
Assembly
FishMMO-AuthShared.dll

Holds per-connection encryption state including directional AES keys, nonce contexts, and negotiated protocol version. Keys are established via X25519 ECDH key agreement during the handshake phase.

Thread-safety: nonce contexts use Interlocked internally for counter operations.

public class ConnectionEncryptionData : IDisposable
Inheritance
ConnectionEncryptionData
Implements
Inherited Members

Constructors

ConnectionEncryptionData(byte[])

Initializes a new instance storing the peer's public key. Directional keys and nonce contexts remain null until PromoteToDirectional(SessionKeys) is called after the X25519 ECDH key agreement completes.

public ConnectionEncryptionData(byte[] publicKey)

Parameters

publicKey byte[]

The peer's X25519 public key (32 bytes).

Properties

AgreedVersion

Negotiated protocol version for this connection. Set during the handshake and used in AAD construction for all subsequent messages. Defaults to ProtocolVersion for backward compatibility. NOTE: Public setter retained because BaseAuthenticatorCore writes this from the handshake result. This is a simple ushort value (no lock needed).

public ushort AgreedVersion { get; set; }

Property Value

ushort

ClientToServerKey

Directional AES keys derived via HKDF-SHA256. ClientToServerKey is used to decrypt client→server messages. ServerToClientKey is used to encrypt server→client messages.

public byte[]? ClientToServerKey { get; }

Property Value

byte[]

MasterSecret

Master secret derived via X25519 ECDH + HKDF. Null after promotion to directional keys.

public byte[]? MasterSecret { get; }

Property Value

byte[]

PublicKey

The client's X25519 public key received during the handshake.

public byte[]? PublicKey { get; }

Property Value

byte[]

ReceiveNonceCtx

Nonce context for client→server (receive/decrypt) direction. Owns the client prefix and receive counter. Null until PromoteToDirectional(SessionKeys) is called. Private setter prevents external code from bypassing the FishMMO.Auth.Implementation.ConnectionEncryptionData.clearLock that Clear() and NextReceiveNonce() use for thread safety.

public CryptoHelper.GcmNonceContext? ReceiveNonceCtx { get; }

Property Value

CryptoHelper.GcmNonceContext

SendNonceCtx

Nonce context for server→client (send/encrypt) direction. Owns the server prefix and send counter. Null until PromoteToDirectional(SessionKeys) is called. Private setter prevents external code from bypassing the FishMMO.Auth.Implementation.ConnectionEncryptionData.clearLock that Clear() and NextSendNonce() use for thread safety.

public CryptoHelper.GcmNonceContext? SendNonceCtx { get; }

Property Value

CryptoHelper.GcmNonceContext

ServerToClientKey

public byte[]? ServerToClientKey { get; }

Property Value

byte[]

Methods

BuildReceiveNonce(uint)

Builds a receive-direction nonce for a specific sequence number. Use after TryConsumeReceiveSequence(uint) has validated the sequence.

public byte[] BuildReceiveNonce(uint seq)

Parameters

seq uint

Returns

byte[]

BuildSendNonce(uint)

Builds a send-direction nonce for a specific sequence number. Use when the sequence was obtained from NextSendSequence().

public byte[] BuildSendNonce(uint seq)

Parameters

seq uint

Returns

byte[]

Clear()

Zeroes all sensitive key material and disposes nonce contexts. Call before removing the entry from the AccountManager.

public void Clear()

CloneForAsyncWorker()

Creates a snapshot suitable for async worker processing. Key material is shared (read-only after derivation); nonce contexts are SHARED (not cloned) — GcmNonceContext is already thread-safe via Interlocked for all counter operations. Sharing ensures that all workers processing messages for the same connection observe a single monotonic sequence space, preventing both receive-sequence rejection and catastrophic send-nonce reuse.

public ConnectionEncryptionData CloneForAsyncWorker()

Returns

ConnectionEncryptionData

Remarks

IMPORTANT — do not dispose the clone. The returned clone shares GcmNonceContext references with the original. Never call Clear() or Dispose() on the clone — the original owns disposal of all key material and nonce contexts. Calling Clear() on the clone will dispose the shared GcmNonceContext instances, corrupting the original and all other clones derived from it.

Dispose()

Releases all sensitive key material by zeroing buffers and disposing nonce contexts. Suppresses finalization since the work is done eagerly.

public void Dispose()

~ConnectionEncryptionData()

Zeroes all sensitive key material and disposes nonce contexts. Called by the finalizer as a safety net on the ORIGINAL instance only. Clones share nonce context references — their finalizer must NOT dispose them.

protected ~ConnectionEncryptionData()

NextReceiveNonce()

Builds the next client→server GCM nonce and advances the receive counter.

public (byte[] Nonce, uint Sequence) NextReceiveNonce()

Returns

(byte[] Nonce, uint Sequence)

Tuple of (12-byte nonce, sequence number).

Remarks

The lock is held across both the reference read and the NextNonce() call to prevent a concurrent Clear() from disposing the context mid-operation.

NextSendNonce()

Builds the next server→client GCM nonce and advances the send counter.

public (byte[] Nonce, uint Sequence) NextSendNonce()

Returns

(byte[] Nonce, uint Sequence)

Tuple of (12-byte nonce, sequence number).

Remarks

The lock is held across both the reference read and the NextNonce() call to prevent a concurrent Clear() from disposing the context mid-operation.

NextSendSequence()

Atomically increments and returns the next send sequence number without building a nonce. Use when the sequence is needed for AAD construction and the nonce will be built separately via BuildSendNonce(uint).

public uint NextSendSequence()

Returns

uint

The next send sequence number.

PromoteToDirectional(SessionKeys)

Promotes the stored state into directional keys and nonce contexts using derived session keys. Creates GcmNonceContext instances that own their respective prefixes and enforce nonce uniqueness per direction.

public void PromoteToDirectional(CryptoHelper.SessionKeys keys)

Parameters

keys CryptoHelper.SessionKeys

TryConsumeReceiveSequence(uint)

Attempts to consume an expected receive sequence number. Returns true and advances the counter if seq is exactly one greater than the current counter. Returns false for duplicates or gaps.

public bool TryConsumeReceiveSequence(uint seq)

Parameters

seq uint

The expected incoming sequence number to consume.

Returns

bool

true if consumed; false otherwise.

TryConsumeReceiveSequenceRange(uint, uint)

Atomically consumes a contiguous receive-sequence range [baseSeq, baseSeq + count - 1]. Use when a single logical message spans multiple wire-level sequence numbers (e.g. multi-field encrypted payloads) so that a partial failure cannot leave the counter advanced part-way through.

public bool TryConsumeReceiveSequenceRange(uint baseSeq, uint count)

Parameters

baseSeq uint

First sequence in the range.

count uint

Number of consecutive sequences; must be > 0.

Returns

bool

true on success; false on duplicate/gap/exhaustion.