pub struct Session { /* private fields */ }std only.Expand description
Session - virtual association between two endpoints
Implementations§
Source§impl Session
impl Session
Sourcepub fn new(
session_id: SessionId,
shared_secret: &[u8; 32],
peer_side: bool,
) -> Result<Self, CoreError>
pub fn new( session_id: SessionId, shared_secret: &[u8; 32], peer_side: bool, ) -> Result<Self, CoreError>
Create a new session with given shared secret
Sourcepub fn from_derived(
session_id: SessionId,
crypto: CryptoState,
scheduler_mode: SchedulerMode,
traffic_secret: [u8; 32],
is_server: bool,
) -> Self
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.
Sourcepub fn resume(
session_id: SessionId,
resumption_secret: &[u8; 32],
peer_side: bool,
) -> Result<Self, CoreError>
pub fn resume( session_id: SessionId, resumption_secret: &[u8; 32], peer_side: bool, ) -> Result<Self, CoreError>
Resume a session using resumption secret (0-RTT)
Sourcepub fn current_outbound_cid(&self) -> [u8; 8]
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.
Sourcepub fn outbound_cid_index(&self) -> u64
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.
Sourcepub fn advance_outbound_cid(&self) -> [u8; 8]
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).
Sourcepub fn note_migration_path(&self, path_id: u8) -> Option<CidSlide>
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.
Sourcepub fn set_cid_slide_tx(&self, tx: UnboundedSender<CidSlide>)
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.
Sourcepub fn signal_cid_slide(&self, slide: CidSlide)
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).
Sourcepub fn inbound_window_cids(&self) -> Vec<[u8; 8]>
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.
Sourcepub fn state(&self) -> SessionState
pub fn state(&self) -> SessionState
Get current state
Sourcepub fn set_state(&self, new_state: SessionState)
pub fn set_state(&self, new_state: SessionState)
Transition to a new state
Sourcepub fn open_stream(&self) -> Arc<Stream>
pub fn open_stream(&self) -> Arc<Stream>
Open a new stream
Sourcepub fn close_stream(&self, stream_id: StreamId) -> bool
pub fn close_stream(&self, stream_id: StreamId) -> bool
Close a stream
Sourcepub fn stream_count(&self) -> u32
pub fn stream_count(&self) -> u32
Get number of active streams
Sourcepub fn replay_rejected_total(&self) -> u64
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.
Sourcepub fn current_epoch(&self) -> u8
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.
Sourcepub fn is_server(&self) -> bool
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.
Sourcepub fn rekey(&self) -> Result<u8, CoreError>
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.
Sourcepub fn send_invocations(&self) -> u64
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.
Sourcepub fn rekey_threshold(&self) -> u64
pub fn rekey_threshold(&self) -> u64
The send-invocation high-watermark at which the pump auto-rekeys.
Sourcepub fn set_rekey_threshold(&self, n: u64)
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.
Sourcepub fn padding_policy(&self) -> PaddingPolicy
pub fn padding_policy(&self) -> PaddingPolicy
The active anti-fingerprint size-padding policy (WIRE v6, deliverable (c)).
Default PaddingPolicy::None (shaping is opt-in).
Sourcepub fn set_padding_policy(&self, policy: PaddingPolicy)
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.
Sourcepub fn send_jitter(&self) -> Duration
pub fn send_jitter(&self) -> Duration
The send-timing jitter ceiling as a Duration (WIRE v6, deliverable (d)).
Duration::ZERO = jitter off (default).
Sourcepub fn set_jitter_ms(&self, ms: u32)
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.
Sourcepub fn cover_interval(&self) -> Duration
pub fn cover_interval(&self) -> Duration
The cover-traffic floor interval as a Duration (WIRE v6, deliverable (e)).
Duration::ZERO = cover off (default).
Sourcepub fn set_cover_interval_ms(&self, ms: u32)
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.
Sourcepub fn send_needs_rekey(&self) -> bool
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.
Sourcepub fn rekey_unconfirmed(&self) -> bool
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.
Sourcepub fn decrypt_packet_accepting_rekey(
&self,
header: &PacketHeader,
ciphertext: &[u8],
extensions: &[u8],
) -> Result<Vec<u8>, CoreError>
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: ordinarydecrypt_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 forgedheader.epochfails the AEAD open, nothing is committed, and the session does not desync. The bound caps an attacker to at mostMAX_REKEY_CATCHUPHKDF steps per spoofed packet.- anything else (behind current, more than
MAX_REKEY_CATCHUPahead, or epoch saturated): rejected. Over a reliable transport the sender retransmits at the then-current epoch, so no data is lost.
Sourcepub fn ratchet_to_epoch(&self, target: u8) -> Result<(), CoreError>
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).
Sourcepub fn validated_paths(&self) -> Vec<u8> ⓘ
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).
Sourcepub fn path_state(&self, path_id: u8) -> Option<PathStateKind>
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.
Sourcepub fn begin_path_validation(&self, path_id: u8) -> Option<[u8; 32]>
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).
Sourcepub fn complete_path_validation(&self, path_id: u8, response: &[u8]) -> bool
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.
Sourcepub fn mark_path_seen(&self, path_id: u8)
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.
Sourcepub fn pacer(&self) -> Arc<Pacer>
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).
Sourcepub fn on_packet_sent(&self, bytes: u64)
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).
Sourcepub fn on_packet_acked(&self, sample: DeliverySample) -> u64
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.
Sourcepub fn on_packet_lost(&self, bytes: u64)
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.
Sourcepub fn reset_congestion(&self)
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.
Sourcepub fn bbr_state(&self) -> BbrState
pub fn bbr_state(&self) -> BbrState
Current BBR congestion-control state. Observability / test hook — lets
callers confirm a loss drove the estimator into FastRecovery.
Sourcepub fn bandwidth_snapshot(&self) -> BandwidthSnapshot
pub fn bandwidth_snapshot(&self) -> BandwidthSnapshot
Read a snapshot of the bandwidth / pacing estimator. Cheap; held over a single mutex lock.
Sourcepub fn send_notifier(&self) -> Arc<Notify>
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.
Sourcepub fn notify_outbound_ready(&self)
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.
Sourcepub fn next_send_pn(&self) -> PacketNumber
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.
Sourcepub fn peek_send_pn(&self) -> PacketNumber
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).
Sourcepub fn current_send_path_id(&self) -> u8
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.
Sourcepub fn next_migration_path_id(&self) -> u8
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).
Sourcepub fn encrypt_packet(
&self,
header: &PacketHeader,
plaintext: &[u8],
extensions: &[u8],
) -> Result<Vec<u8>, CoreError>
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.
Sourcepub fn decrypt_packet(
&self,
header: &PacketHeader,
ciphertext: &[u8],
extensions: &[u8],
) -> Result<Vec<u8>, CoreError>
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.
Sourcepub fn protect_packet(
&self,
packet: &PhantomPacket,
) -> Result<Vec<u8>, CoreError>
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.
Sourcepub fn parse_protected(&self, bytes: &[u8]) -> Result<PhantomPacket, CoreError>
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.
Sourcepub fn scheduler(&self) -> &Arc<Scheduler>
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.
Sourcepub fn set_resumption_secret(&self, secret: [u8; 32])
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.
Sourcepub fn resumption_secret(&self) -> Option<[u8; 32]>
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().
Sourcepub fn can_resume(&self) -> bool
pub fn can_resume(&self) -> bool
Check if session can be resumed (has resumption secret)
Sourcepub fn resumption_hint(&self) -> Option<([u8; 32], [u8; 32])>
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.
Sourcepub fn update_activity(&self)
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.
Sourcepub fn mark_keepalive_outstanding(&self)
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.
Sourcepub fn keepalive_outstanding(&self) -> bool
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.
Sourcepub fn is_expired(&self, timeout: Duration) -> bool
pub fn is_expired(&self, timeout: Duration) -> bool
Check if session is expired
Sourcepub fn last_activity_elapsed(&self) -> Duration
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).
Sourcepub fn liveness_config(&self) -> LivenessConfig
pub fn liveness_config(&self) -> LivenessConfig
Current path-liveness thresholds (P4.3).
Sourcepub fn set_liveness_config(&self, cfg: LivenessConfig)
pub fn set_liveness_config(&self, cfg: LivenessConfig)
Override the path-liveness thresholds (P4.3). Rust-only test/advanced hook,
mirroring set_rekey_threshold.