# NFT-SEC-03 — MAVLink 2.0 signing rejection

**Task**: AZ-438_nft_sec_03_mavlink_signing
**Name**: AP rejects unsigned / wrong-key / replayed messages (AC-NEW-11 / D-C8-9)
**Description**: Implement NFT-SEC-03 — Tier-1 OR Tier-2; AP-only; runner sends each of (a) unsigned `GPS_INPUT`, (b) signed-with-wrong-key, (c) replayed-from-tlog; AP SITL rejects each via `BAD_SIGNATURE` STATUSTEXT.
**Complexity**: 3 points
**Dependencies**: AZ-406, AZ-407 (mavlink-passkey)
**Component**: Blackbox Tests / Security (epic AZ-262)
**Tracker**: AZ-438
**Epic**: AZ-262 (E-BBT)

## Problem

The MAVLink 2.0 signing handshake (D-C8-9) only protects the channel if AP correctly rejects un-signed / wrong-key / replayed messages. NFT-SEC-03 directly probes those rejection paths.

## Outcome

- pytest scenario at `e2e/tests/security/test_nft_sec_03_mavlink_signing.py`. AP-only.
- Three sub-cases (sent in order; pause between each):
  - (a) Runner crafts and sends an unsigned `GPS_INPUT` to AP SITL.
  - (b) Runner crafts and sends a `GPS_INPUT` signed with a wrong-key passkey.
  - (c) Runner replays a captured signed `GPS_INPUT` from a prior `.tlog` (counter-replay attack).
- For each: assert AP emits `BAD_SIGNATURE` STATUSTEXT within ≤500 ms; AP's GPS state does NOT update from these messages.

## Scope

### Included
- All three sub-cases.
- mavproxy STATUSTEXT capture for `BAD_SIGNATURE`.
- AP GPS state read for the no-update assertion.

### Excluded
- iNav variant — N/A; iNav has no MSP signing.
- Signing handshake setup — owned by FT-P-09-AP (AZ-416).

## Acceptance Criteria

**AC-1: tier guard**
Given `fc_adapter == inav`
Then the test SKIPs.

**AC-2: unsigned rejection**
Given the runner sends an unsigned `GPS_INPUT`
Then AP emits `BAD_SIGNATURE` (or equivalent rejection STATUSTEXT) within ≤500 ms; AP's `GLOBAL_POSITION_INT` does NOT update.

**AC-3: wrong-key rejection**
Given the runner sends a `GPS_INPUT` signed with a wrong-key passkey
Then same as AC-2.

**AC-4: replayed-message rejection**
Given the runner replays a captured signed `GPS_INPUT` from a prior `.tlog`
Then same as AC-2 (signing's monotonic counter prevents replay acceptance).

**AC-5: vio_strategy parameterization**
Given conftest parameterization
Then the scenario runs once per `vio_strategy` (the SUT's VIO is irrelevant here — runner is the sender).

## System Under Test Boundary

The "SUT" here is the AP's signing-acceptance behavior. The probe is at the AP boundary.

- **Allowed**: runner-crafted MAVLink messages (a public protocol surface).
- **Forbidden**: monkeypatching AP's signing logic.

## Constraints

- The `BAD_SIGNATURE` STATUSTEXT regex matches the AP-emitted text (e.g. "MAVLink: BAD_SIGNATURE" or equivalent — the exact string is canonical to AP and recorded in the test fixture).

## Document Dependencies

- `_docs/02_document/tests/security-tests.md` § NFT-SEC-03
- `_docs/02_document/tests/test-data.md` § Security (NFT-SEC-03 row)
