# Operator Command Auth: Signature + Replay-Protection + Session Validation

**Task**: AZ-678_operator_bridge_command_auth
**Name**: Validate operator command signature, replay-protection sequence, and session token before dispatch
**Description**: Every incoming operator command MUST pass three checks before any business logic runs: (1) authentication signature over `(session_token, sequence_number, payload)`, (2) replay-protection sequence number is monotonically increasing per session, (3) session token is currently valid. Modem-link encryption is not sufficient (per `architecture.md §5` + Q9).
**Complexity**: 5 points
**Dependencies**: AZ-640_initial_structure, AZ-675_telemetry_stream_grpc_server
**Component**: operator_bridge
**Tracker**: AZ-678
**Epic**: AZ-628

## Problem

Operator commands shape the drone's behaviour (target-follow start, mission resume, safety-override). A hostile injection on the operator-link could otherwise direct the UAV to a forbidden target or release a safety. The authoritative principle ("operator commands authenticated, signed, replay-protected, session-bound") is committed in `architecture.md §5`; the exact scheme is open (`§8 Q9`) — this task implements the validator layer in a scheme-agnostic shape and a default HMAC-SHA256 scheme as the placeholder concrete implementation, behind a trait so Q9 resolution can swap it cleanly.

## Outcome

- Trait `OperatorCommandValidator { fn validate(&self, cmd: &SignedCommand) -> Result<ValidatedCommand, AuthError> }` in `shared::contracts::operator_auth`.
- Default impl `HmacOperatorValidator` using SHA-256 HMAC over `(session_token || sequence_number || payload_bytes)`.
- Per-session replay-protection: highest-seen sequence number stored in-process; rejection on equal-or-less.
- Session token validity check against an in-process session registry (sessions added on Ground Station auth; expired after `session_ttl_secs`).
- All rejections counted by reason: `signature_invalid`, `replay_detected`, `session_unknown`, `session_expired`.
- Health: `auth_rejections_total{reason}`; sustained signature failures → health red.

## Scope

### Included
- Validator trait + HMAC default impl.
- Per-session sequence-number tracker.
- Session registry (in-process, in-memory).
- Rejection-reason metrics.

### Excluded
- The transport (Ground Station → operator_bridge); this task validates already-deserialized `SignedCommand`s.
- Session creation handshake from Ground Station (handled at session establishment; this task only consumes sessions).
- Dispatch to `scan_controller` / `mission_executor` (tasks 41 + 42).

## Acceptance Criteria

**AC-1: Valid signed command passes**
Given a `SignedCommand` with correct HMAC over `(token, seq, payload)` and a known session with `seq > last_seen`
When `validate(cmd)` runs
Then it returns `Ok(ValidatedCommand)`; `last_seen` advances to `seq`.

**AC-2: Invalid signature rejected**
Given a `SignedCommand` whose HMAC does not match the computed value
When `validate(cmd)` runs
Then it returns `Err(AuthError::SignatureInvalid)`; `auth_rejections_total{reason: signature_invalid}` increments; sequence number is NOT advanced.

**AC-3: Replay detected**
Given a session with `last_seen = 10`
When a command arrives with `sequence_number = 10` (or `< 10`)
Then it returns `Err(AuthError::ReplayDetected)`; `auth_rejections_total{reason: replay_detected}` increments.

**AC-4: Unknown / expired session rejected**
Given a command bearing a session token not in the registry (or whose TTL has elapsed)
When `validate(cmd)` runs
Then it returns `Err(AuthError::SessionUnknown)` or `SessionExpired`; appropriate counter increments.

**AC-5: Sustained signature failures escalate health**
Given `auth_rejections_total{reason: signature_invalid}` exceeds `signature_failure_red_threshold` per minute (config)
When health is sampled
Then health returns red and surfaces the failure rate.

## Non-Functional Requirements

**Performance**
- `validate` ≤1 ms p99.

**Security**
- Reject-then-log; never log the raw payload of a rejected command at info level (size-cap + redact).
- No timing oracle — use constant-time HMAC compare.

## Contract

- Canonical typed model: `data_model.md §OperatorCommand`, `§SignedCommand`. Open Q9 (`architecture.md §8`) is acknowledged; this task ships a working HMAC default and a trait so resolution can swap.

## Runtime Completeness

- **Named capability**: operator command authentication + replay-protection + session binding.
- **Production code that must exist**: real HMAC validator; real per-session monotonic counter; real session registry; constant-time compare.
- **Unacceptable substitutes**: "accept all signed commands as valid" or sequence number tracked globally (cross-session replay) are unacceptable.
