envelope

package
v1.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 12, 2026 License: AGPL-3.0 Imports: 5 Imported by: 0

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

View Source
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

func EncryptFrame(store *keyexchange.Store, dst uint32, plaintext []byte) ([]byte, error)

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL