Documentation
¶
Overview ¶
Package envelope is L6 — the per-peer AEAD framing layer. It runs the AEAD wrap/unwrap and replay-window check on every tunnel frame.
envelope is a downward consumer of L5 (keyexchange): it reads per-peer Crypto state from keyexchange.Store, but does NOT own that state. Ownership of Crypto, the salvage ring, the replay-bitmap, and the MaxCryptoPeers cap lives at L5.
Per docs/architecture/01-LAYERS.md L6:
Role: AEAD encrypt and decrypt every frame; per-peer replay detection. Owns: NOTHING persistent — framing functions are stateless and operate on Crypto values supplied by L5. Consumes: L1, L2, L3 (primitives), L5 (key state). Exposes: EncryptFrame(store, dst, plaintext) → frame, DecryptFrame(store, frame) → DecryptResult.
Per docs/architecture/03-INVARIANTS.md §3 (lock graph): the only locks taken here are keyexchange.Crypto.ReplayMu (LEAF). They are never nested with any TunnelManager-side mutex.
Per docs/architecture/03-INVARIANTS.md §9 (horizontal-incest): L6 → L5 is a downward import (allowed). envelope/ MUST NOT be imported by keyexchange/ — that would be upward.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrReplay = errors.New("envelope: nonce replay detected") ErrOutsideWindow = errors.New("envelope: counter outside replay window") ErrAEAD = errors.New("envelope: AEAD authentication failed") ErrTooShort = errors.New("envelope: frame too short") )
Framing-level errors. Pure verdicts on the wire-format / AEAD path — they do not depend on L5 key-state policy. Key-state errors (ErrNoKey, ErrNotReady) live with the Store at L5 (keyexchange).
Functions ¶
func EncryptFrame ¶
EncryptFrame encrypts plaintext using the Crypto installed for dst in store and returns the on-wire frame:
[PILS magic(4)][localNodeID(4)][nonce(12)][ciphertext+GCM tag]
The local node ID is written into both the frame header and the AEAD AAD (H3 fix — binds sender identity into authentication).
func EncryptWith ¶
func EncryptWith(store *keyexchange.Store, c *keyexchange.Crypto, plaintext []byte) []byte
EncryptWith is the variant used by callers that already hold the Crypto pointer (e.g. flushPending, keepaliveSweep, replaySalvage). Bypasses the map lookup. Caller must ensure c is non-nil and Ready.
Types ¶
type DecryptResult ¶
type DecryptResult struct {
// Plaintext is the AEAD-Open output (nil on failure / replay).
Plaintext []byte
// PeerNodeID is the sender ID parsed from the frame header.
PeerNodeID uint32
// Counter is the nonce counter pulled from the frame.
Counter uint64
// Err categorises the outcome:
// nil — success.
// keyexchange.ErrNoKey — no Crypto installed for this peer (caller
// should trigger a rekey request).
// ErrReplay — replay-window check rejected the nonce.
// ErrOutsideWindow — counter older than the window (likely peer
// replaced their crypto and counter restarted from low).
// ErrAEAD — AEAD-Open failed (key divergence or corruption).
// ErrTooShort — frame structurally invalid.
Err error
// MaxRecvNonce is the recv-nonce high-water-mark observed under
// ReplayMu, captured for caller logging without re-locking.
MaxRecvNonce uint64
}
DecryptResult is the outcome of DecryptFrame.
func DecryptFrame ¶
func DecryptFrame(store *keyexchange.Store, data []byte) DecryptResult
DecryptFrame parses an inbound encrypted frame, runs replay check, and AEAD-Open. The frame argument is the bytes AFTER the PILS magic (i.e. starting at the 4-byte sender nodeID — matches the readLoop's `data` parameter to handleEncrypted).
Returns a DecryptResult describing the outcome. The caller (L5/L7) consults Result.Err to decide rekey requests, drop policy, etc.
On AEAD-Open failure DecryptFrame:
- rolls back the speculative replay-bit (UndoReplayBit) so future legitimate frames at the same counter can still be decoded;
- increments c.DecryptFailCount under c.ReplayMu (kept on the Crypto rather than under Store.mu — c.ReplayMu is leaf-level so this preserves the lock graph invariant).
The grace-gated drop decision (ShouldDropOnDecryptFail) lives on keyexchange.Store; this function only signals via DecryptResult.Err.