Skip to main content

Session

Struct Session 

Source
pub struct Session { /* private fields */ }
Available on crate feature std only.
Expand description

Session - virtual association between two endpoints

Implementations§

Source§

impl Session

Source

pub fn new( session_id: SessionId, shared_secret: &[u8; 32], peer_side: bool, ) -> Result<Self, CoreError>

Create a new session with given shared secret

Source

pub fn from_derived( session_id: SessionId, crypto: CryptoState, scheduler_mode: SchedulerMode, traffic_secret: [u8; 32], is_server: bool, ) -> Self

Create session from a pre-derived crypto state (e.g., after handshake).

traffic_secret is the master from which the supplied crypto was derived — it seeds the rekey HKDF chain. is_server records which side of the handshake we are; rekey re-derives keys with the same side so per-direction layout is preserved.

Source

pub fn resume( session_id: SessionId, resumption_secret: &[u8; 32], peer_side: bool, ) -> Result<Self, CoreError>

Resume a session using resumption secret (0-RTT)

Source

pub fn current_outbound_cid(&self) -> [u8; 8]

The envelope ConnId this peer currently stamps on outbound UDP datagrams (ε / WIRE v5). At the current outbound CID index (0 until the first migrate()) this is CID_0 of the outbound chain — and it is exactly inbound_window_cids()[0] for the peer, which is how the demux routes it. TCP/embedded transports are socket-routed and ignore this.

Source

pub fn outbound_cid_index(&self) -> u64

This peer’s current outbound CID migration index (ε / WIRE v5). 0 until the first migrate(); exposed for the migration wiring (P4) and diagnostics.

Source

pub fn advance_outbound_cid(&self) -> [u8; 8]

Rotate the outbound CID one step (ε / WIRE v5; called on migrate()): advance the outbound index and return the new CID_{i+1} for the transport to stamp. The new CID is independent-random vs the previous one, so an observer cannot link the pre- and post-migration flows; it stays inside the peer’s pre-registered inbound window for up to K migrations (the demux window slides post-AEAD beyond that).

Source

pub fn note_migration_path(&self, path_id: u8) -> Option<CidSlide>

Track the peer’s migration via its path_id and, on a NEW (forward) path, slide the inbound CID demux window one step (ε / WIRE v5, P4b). Returns the CidSlide to apply at the demux, or None if path_id is not newer — a reordered-old or duplicate packet, or a passive rebind that did not rotate the CID, none of which advance the index. Call post-AEAD only (the path_id is then authenticated). The single-step advance keeps the window tracking the peer’s outbound index (both bump once per migrate()); the leading window K absorbs any transient lag from a multi-hop jump.

Source

pub fn set_cid_slide_tx(&self, tx: UnboundedSender<CidSlide>)

Install the demux slide channel (ε / WIRE v5, P4b) — called once by the server’s accept path so handle_packet can signal inbound-window slides.

Source

pub fn signal_cid_slide(&self, slide: CidSlide)

Signal the demux to apply a CidSlide (ε / WIRE v5, P4b). A no-op when no slide channel is installed (the client and socket-routed transports).

Source

pub fn inbound_window_cids(&self) -> Vec<[u8; 8]>

The inbound CIDs the demux should route to this session (ε / WIRE v5): the window [highest_seen − T, highest_seen + K] over this peer’s inbound chain. At session establishment (highest_seen = 0, trailing saturates) this is the leading lookahead [CID_0 .. CID_K] (K + 1 entries). The server registers these in its RouteTable; a datagram whose ConnId is in the set routes to this session. The window slide on migrate() is P4.

Source

pub fn id(&self) -> &SessionId

Get session ID

Source

pub fn state(&self) -> SessionState

Get current state

Source

pub fn set_state(&self, new_state: SessionState)

Transition to a new state

Source

pub fn open_stream(&self) -> Arc<Stream>

Open a new stream

Source

pub fn get_stream(&self, stream_id: StreamId) -> Option<Arc<Stream>>

Get an existing stream

Source

pub fn close_stream(&self, stream_id: StreamId) -> bool

Close a stream

Source

pub fn stream_count(&self) -> u32

Get number of active streams

Source

pub fn replay_rejected_total(&self) -> u64

Total number of replayed packets rejected by the sliding-window check across all streams in this session. Intended for the replay_rejected_total metric.

Source

pub fn current_epoch(&self) -> u8

Current rekey generation (Phase 1.5). Starts at 0; each successful rekey increments by one. Carried on the wire in PacketHeader.epoch so the peer can match the right derived key.

Source

pub fn is_server(&self) -> bool

Whether this session is acting as the server side. Determined at construction; required for re-deriving per-direction keys on rekey.

Source

pub fn rekey(&self) -> Result<u8, CoreError>

Mid-session key rotation (Phase 1.5).

Derives the next traffic secret from the current one via HKDF-Expand(current, "phantom-rekey-v1", 32) and builds a fresh CryptoState under that secret. The new state is installed via an atomic ArcSwap::store, so concurrent encrypt/decrypt calls observe either the old or the new state — never a partially-written in-between. The previous traffic secret is explicitly zeroed before being overwritten.

Returns the new epoch (1, 2, 3, …). Wraps an error if the epoch counter has saturated u8::MAX (after 255 successful rekeys — equivalent to ~5 days at the default 30-minute cadence; long-lived sessions are expected to reconnect rather than wrap).

Wire signalling: callers that want the peer to follow this rekey emit a packet whose header carries the new epoch (with the PacketFlags::REKEY flag set). Receivers respond by calling rekey() themselves once they see the bump — keeping both ends in lockstep.

Source

pub fn send_invocations(&self) -> u64

Send-side AEAD invocation count for the current epoch (resets to 0 on each rekey). Drives send_needs_rekey.

Source

pub fn rekey_threshold(&self) -> u64

The send-invocation high-watermark at which the pump auto-rekeys.

Source

pub fn set_rekey_threshold(&self, n: u64)

Override the auto-rekey high-watermark (default REKEY_SOFT_LIMIT). Clamped to >= 1. Rust-only — primarily for tests/soak harnesses that need to exercise mid-session rekey without sending 2^47 packets.

Source

pub fn padding_policy(&self) -> PaddingPolicy

The active anti-fingerprint size-padding policy (WIRE v6, deliverable (c)). Default PaddingPolicy::None (shaping is opt-in).

Source

pub fn set_padding_policy(&self, policy: PaddingPolicy)

Set the size-padding policy. When PaddingPolicy::Padme, the send path pads every packet up to a PADÉ bucket (inside the AEAD) before sealing, so the datagram size no longer tracks the payload size — at a bounded (≈ ≤12% worst-case) bandwidth cost.

Source

pub fn send_jitter(&self) -> Duration

The send-timing jitter ceiling as a Duration (WIRE v6, deliverable (d)). Duration::ZERO = jitter off (default).

Source

pub fn set_jitter_ms(&self, ms: u32)

Set the send-timing jitter ceiling in milliseconds (0 = off). When set, the send path waits a uniform random [0, ms] ms before each packet so inter-packet timing no longer tracks the application’s writes, at a cost of up to ms of added latency per packet.

Source

pub fn cover_interval(&self) -> Duration

The cover-traffic floor interval as a Duration (WIRE v6, deliverable (e)). Duration::ZERO = cover off (default).

Source

pub fn set_cover_interval_ms(&self, ms: u32)

Set the cover-traffic floor interval in milliseconds (0 = off). When set, the pump maintains a minimum outbound packet rate of 1000 / ms packets/sec by emitting an ENCRYPTED | COVER dummy packet whenever no packet has gone out for ms — hiding idle/active patterns and volume, at a steady bandwidth cost.

Source

pub fn send_needs_rekey(&self) -> bool

True once the send direction has crossed the rekey high-watermark and the epoch has room to advance. The data pump checks this before each application send and, when set, rekeys + flags the packet REKEY so the peer follows via the authenticated epoch bump.

Source

pub fn rekey_unconfirmed(&self) -> bool

T5.5(b) — true while a locally-initiated rekey is still unacknowledged by the peer. The send path OR-s PacketFlags::REKEY into every outbound header while this holds, so the catch-up gate in decrypt_packet_accepting_rekey can safely reject an unflagged forward epoch: an honest not-yet-confirmed sender always re-advertises the flag. See the field doc for the relaxed ordering rationale.

Source

pub fn decrypt_packet_accepting_rekey( &self, header: &PacketHeader, ciphertext: &[u8], extensions: &[u8], ) -> Result<Vec<u8>, CoreError>

Decrypt a packet, transparently following an authenticated forward rekey of up to MAX_REKEY_CATCHUP epochs (C1).

  • header.epoch == current: ordinary decrypt_packet.
  • current < header.epoch <= current + MAX_REKEY_CATCHUP: derive the candidate key that many epochs ahead and trial-decrypt. Only on AEAD success — i.e. once the epoch bump is proven authentic — is the rekey committed and the replay window consulted (Invariant 4 ordering preserved). A forged header.epoch fails the AEAD open, nothing is committed, and the session does not desync. The bound caps an attacker to at most MAX_REKEY_CATCHUP HKDF steps per spoofed packet.
  • anything else (behind current, more than MAX_REKEY_CATCHUP ahead, or epoch saturated): rejected. Over a reliable transport the sender retransmits at the then-current epoch, so no data is lost.
Source

pub fn ratchet_to_epoch(&self, target: u8) -> Result<(), CoreError>

Advance to a specific target epoch by repeatedly applying the rekey HKDF chain. Used by the receive path to “catch up” when it sees a packet from a higher epoch than the locally known one. Refuses to go backwards (a lower target than current returns Ok without changes).

Source

pub fn validated_paths(&self) -> Vec<u8>

Snapshot of currently Validated path ids (those that completed a challenge/response round trip, plus the always-validated path 0).

Source

pub fn path_state(&self, path_id: u8) -> Option<PathStateKind>

State of a specific path within this session. Returns None for path ids the session has never observed.

Source

pub fn begin_path_validation(&self, path_id: u8) -> Option<[u8; 32]>

Register a new path id and immediately issue a 32-byte PATH_CHALLENGE for it. Returns the challenge bytes; the caller must transmit them in a packet with PacketFlags::PATH_VALIDATION set on the new path. Subsequent calls on an already-Validating path re-issue a fresh challenge.

Returns None if the path is in a terminal state (Validated or Failed).

Source

pub fn complete_path_validation(&self, path_id: u8, response: &[u8]) -> bool

Verify a peer’s PATH_VALIDATION response. Returns true if the response matches the in-flight challenge (path is now Validated). Returns false otherwise — the path may have transitioned to Failed.

Source

pub fn mark_path_seen(&self, path_id: u8)

Record that a packet was observed on the path. Cheap to call per-packet — used by the data pump to keep last_packet_seen fresh for the timeout sweep.

Source

pub fn pacer(&self) -> Arc<Pacer>

Shared handle to this session’s outbound rate-limiter. Cheap to clone (Arc). The data pump consults this before every outbound packet; idle by default (Pacer::unlimited).

Source

pub fn on_packet_sent(&self, bytes: u64)

Record that a packet of bytes length is going on the wire. Feeds the BBR-style bandwidth estimator. Cheap (one mutex lock

  • a counter increment).
Source

pub fn on_packet_acked(&self, sample: DeliverySample) -> u64

Record that an ACK arrived with delivery sample sample. The returned u64 is the updated bottleneck bandwidth estimate; we reflect it into the pacer so the outbound rate tracks the peer’s actual receive throughput.

Source

pub fn on_packet_lost(&self, bytes: u64)

Record that a packet of bytes length was lost (no ACK before retransmit timer fired). Drives BBR’s loss-based feedback.

Source

pub fn reset_congestion(&self)

Reset the congestion controller + pacer to startup (Phase 4 / QUIC §9.4): a migration path switch lands on a different network, so the old bottleneck-bandwidth / cwnd estimate must not carry over — inheriting a low RTT/cwnd would trigger a spurious-retransmit storm on the first packets of the new path. Wired by the P4.2 migration switch.

Source

pub fn bbr_state(&self) -> BbrState

Current BBR congestion-control state. Observability / test hook — lets callers confirm a loss drove the estimator into FastRecovery.

Source

pub fn bandwidth_snapshot(&self) -> BandwidthSnapshot

Read a snapshot of the bandwidth / pacing estimator. Cheap; held over a single mutex lock.

Source

pub fn send_notifier(&self) -> Arc<Notify>

Shared handle to the outbound-ready notify. The API-layer data pump awaits this via Notify::notified(); any task with the handle can wake it instantly via Self::notify_outbound_ready.

Source

pub fn notify_outbound_ready(&self)

Wake the data pump’s send loop immediately so it can drain newly- queued packets instead of waiting for the next 10 ms tick. Cheap (a single notify_one()); duplicate calls collapse to one wake.

Source

pub fn next_send_pn(&self) -> PacketNumber

Reserve the next outbound packet number for this direction (① — Phase 4). Strictly monotonic; never reused within a session. Drawn by the data pump for every outbound packet (data, control, path-validation, retransmit) at send time, so the AEAD nonce is never reused.

Source

pub fn peek_send_pn(&self) -> PacketNumber

Read the next send packet number without consuming it. Used by the cover- traffic timer as a lock-free “did we send anything since last tick?” signal (every outbound packet draws a fresh PN via next_send_pn).

Source

pub fn current_send_path_id(&self) -> u8

The client-owned send-side path_id currently stamped on outbound packets (D5 — Phase 4). Defaults to 0 (the implicit handshake path). Bumped by next_migration_path_id on a migration.

Source

pub fn next_migration_path_id(&self) -> u8

Bump the send-side path_id to a fresh value and return it (D5 — Phase 4). Called on each migrate()/rebind so the peer’s source-change detector sees a new path label and issues a PATH_CHALLENGE. Never returns 0 — that id is permanently the always-Validated handshake path, so reusing it would make the peer skip the challenge and the switch could never fire. Also never returns REBIND_VALIDATION_PATH_ID (255), reserved for the M-3 passive-rebind challenge — sharing that slot would let an active-migration echo and a passive-rebind echo resolve each other’s registry entry. Wraps 254 → 1. Reuse of a retired id is nonce-safe because ① took path_id out of the AEAD nonce (nonce = nonce_prefix ‖ packet_number).

Source

pub fn encrypt_packet( &self, header: &PacketHeader, plaintext: &[u8], extensions: &[u8], ) -> Result<Vec<u8>, CoreError>

Encrypt a packet payload.

The AEAD nonce is prefix || packet_number (① — Phase 4), derived from the authenticated header rather than an internal counter, so a failed peer decrypt never desyncs the receiver. The AAD is the 47-byte AAD image of the header (PacketHeader::to_aad_image) — which still binds session_id / epoch / stream_id / path_id even though the wire header is 15 bytes — followed by the packet’s extensions TLV (T4.1), so any wire-level mutation of the header or the extensions invalidates the tag.

Source

pub fn decrypt_packet( &self, header: &PacketHeader, ciphertext: &[u8], extensions: &[u8], ) -> Result<Vec<u8>, CoreError>

Decrypt a packet payload. Performs AEAD verify + per-direction sliding-window replay rejection on the packet number (the window check runs after a successful AEAD open — Invariant 4 — so we never key off un-authenticated packet numbers).

A failed decrypt does NOT desync future decrypts: the AEAD nonce is derived from this packet’s authenticated header fields, so the receiver stays in lock-step with the sender regardless of intervening bad packets.

Source

pub fn protect_packet( &self, packet: &PhantomPacket, ) -> Result<Vec<u8>, CoreError>

Serialise a packet to its header-protected on-wire bytes: the WHOLE 15-byte header span (WIRE v6: [0..15], version byte included) is XOR-masked with this session’s send-direction HP key, keyed by the packet’s ciphertext sample (the first 16 bytes of packet.payload). The data plane calls this instead of to_wire for every post-handshake packet. packet.payload must be AEAD ciphertext (>= the 16-byte tag) — always true for the output of encrypt_packet.

Source

pub fn parse_protected(&self, bytes: &[u8]) -> Result<PhantomPacket, CoreError>

Parse a header-protected wire packet: recover the cleartext header by unmasking the WHOLE 15-byte span (WIRE v6: [0..15], version byte included) with this session’s recv-direction HP key (keyed by the cleartext ciphertext sample), reconstruct the off-wire session_id from this session’s id (ε / WIRE v5), then reassemble the PhantomPacket. The caller still gates on version and runs AEAD on the result — a wire mutation of the masked region unmasks to a wrong header, so the subsequent AEAD open fails (no new oracle). A short / malformed frame returns Err and is dropped by the recv loop.

Source

pub fn scheduler(&self) -> &Arc<Scheduler>

The path scheduler. Vestigial — single-path connection migration means select_paths is never driven on the live data path; exposed for diagnostics / per-leg RTT-loss inspection only. See SchedulerMode.

Source

pub fn set_resumption_secret(&self, secret: [u8; 32])

Set resumption secret for 0-RTT.

If a secret was already set, the previous bytes are explicitly zeroed before being replaced — defense in depth in case set_resumption_secret is called multiple times within a session.

Source

pub fn resumption_secret(&self) -> Option<[u8; 32]>

The resumption secret for 0-RTT, if one has been installed. Rust-only — the FFI surface exposes this via PhantomSession::resumption_hint().

Source

pub fn can_resume(&self) -> bool

Check if session can be resumed (has resumption secret)

Source

pub fn resumption_hint(&self) -> Option<([u8; 32], [u8; 32])>

Extract the resumption hint needed to attempt 0-RTT resume on a future connect (Phase 4.1). Returns Some((session_id_bytes, resumption_secret)) only after a successful handshake — the resumption_secret is set by process_client_hello / process_server_hello once shared key material is in place.

The caller is responsible for storing the tuple alongside the pinned HybridVerifyingKey of the server it was negotiated with. Mixing tickets across servers is a configuration bug — the resumption_secret is server-pinned.

Source

pub fn update_activity(&self)

Update last activity timestamp.

Called on every authenticated inbound packet. Any such packet — the keep-alive PONG, an ACK, or application data — proves the path is alive, so it also clears an outstanding idle keep-alive probe (Direction #3): the probe has been answered, so the liveness sweep no longer needs to treat the path as awaiting a response.

Source

pub fn mark_keepalive_outstanding(&self)

Mark that an idle keep-alive PING was just emitted and is awaiting the peer’s PONG (Direction #3). While outstanding, the liveness sweep treats the path as having a probe in flight even with no reliable data queued, so a download-only path can detect a silently-dead downstream.

Source

pub fn keepalive_outstanding(&self) -> bool

Whether an idle keep-alive PING is currently awaiting a PONG (Direction #3). false once any authenticated inbound packet refreshes activity.

Source

pub fn is_expired(&self, timeout: Duration) -> bool

Check if session is expired

Source

pub fn last_activity_elapsed(&self) -> Duration

Elapsed time since the last authenticated inbound packet — the inbound-silence signal the liveness sweep evaluates (Phase 4 / P4.3).

Source

pub fn liveness_config(&self) -> LivenessConfig

Current path-liveness thresholds (P4.3).

Source

pub fn set_liveness_config(&self, cfg: LivenessConfig)

Override the path-liveness thresholds (P4.3). Rust-only test/advanced hook, mirroring set_rekey_threshold.

Trait Implementations§

Source§

impl Debug for Session

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Drop for Session

Source§

fn drop(&mut self)

On session drop, explicitly zero the master secrets. The CryptoState inside crypto is itself ZeroizeOnDrop, so its session_key is handled there.

Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CompatExt for T

Source§

fn compat(self) -> Compat<T>
where T: Sized,

Applies the Compat adapter by value. Read more
Source§

fn compat_ref(&self) -> Compat<&T>

Applies the Compat adapter by shared reference. Read more
Source§

fn compat_mut(&mut self) -> Compat<&mut T>

Applies the Compat adapter by mutable reference. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FutureExt for T

Source§

fn with_context(self, otel_cx: Context) -> WithContext<Self>

Attaches the provided Context to this type, returning a WithContext wrapper. Read more
Source§

fn with_current_context(self) -> WithContext<Self>

Attaches the current Context to this type, returning a WithContext wrapper. Read more
Source§

impl<T, UT> HandleAlloc<UT> for T
where T: Send + Sync,

Source§

fn new_handle(value: Arc<T>) -> Handle

Create a new handle for an Arc value Read more
Source§

unsafe fn clone_handle(handle: Handle) -> Handle

Clone a handle Read more
Source§

unsafe fn consume_handle(handle: Handle) -> Arc<T>

Consume a handle, getting back the initial Arc<> Read more
Source§

unsafe fn get_arc(handle: Handle) -> Arc<Self>

Get a clone of the Arc<> using a “borrowed” handle. Read more
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more