// Security message types for PEAT Protocol Device Authentication (PKI)
// Version: 1.0.0
// Related: ADR-006, Issue #160

syntax = "proto3";

package cap.security.v1;

import "common.proto";

// Device type classification per ADR-006
enum DeviceType {
  DEVICE_TYPE_UNSPECIFIED = 0;
  DEVICE_TYPE_UAV = 1;           // Unmanned Aerial Vehicle
  DEVICE_TYPE_UGV = 2;           // Unmanned Ground Vehicle
  DEVICE_TYPE_USV = 3;           // Unmanned Surface Vehicle
  DEVICE_TYPE_SOLDIER_SYSTEM = 4; // Soldier/Dismounted System
  DEVICE_TYPE_C2_STATION = 5;     // Command & Control Station
  DEVICE_TYPE_RELAY_NODE = 6;     // Relay/Communication Node
  DEVICE_TYPE_SENSOR_NODE = 7; // Sensor Node (static)
}

// Hierarchy level for organizational units
//
// 4-tier rigid-schema model per ADR-066. Tier 5+ deferred to ADR-067
// (parametric N-tier aggregation, planned).
//
// ============================================================================
// WIRE-FORMAT BREAK (ADR-066, peat#904 Phases 1+2)
// ============================================================================
// This enum was previously named after military echelons. Value 4 has been
// rebound from its prior meaning, and values 5–6 have been removed entirely.
//
// Old → New semantic mapping:
//   1: SQUAD     → CELL        (renamed; meaning equivalent — squad and cell
//                                  were the same concept under different names,
//                                  see ADR-066 §"Internal inconsistency")
//   2: PLATOON   → COHORT      (renamed; meaning equivalent)
//   3: COMPANY   → FEDERATION  (renamed; meaning equivalent)
//   4: BATTALION → COALITION   (REBOUND — value 4 retained but the tier name
//                                  changed from a US Army echelon label to the
//                                  abstract "coalition" tier. Pre-rename
//                                  DeviceIdentity records carrying value 4 with
//                                  battalion-equivalent membership are still
//                                  semantically valid at the same tier.)
//   5: BRIGADE   → REMOVED     (tier 5+ deferred to ADR-067; pre-rename records
//                                  carrying value 5 decode as UNSPECIFIED on
//                                  proto3 deserialization)
//   6: DIVISION  → REMOVED     (same as 5)
//
// Wire compatibility: peat-schema bumps to a new major version (per ADR-066's
// hard-rename + no-aliases policy). Mixed-version peers cannot mutually decode.
// Pre-rename DeviceIdentity records persisted as binary protobuf will decode
// with values 5–6 silently becoming UNSPECIFIED; operators with such records
// must re-provision device identities before joining a post-rename mesh.
enum HierarchyLevel {
  HIERARCHY_LEVEL_UNSPECIFIED = 0;
  HIERARCHY_LEVEL_CELL = 1;
  HIERARCHY_LEVEL_COHORT = 2;
  HIERARCHY_LEVEL_FEDERATION = 3;
  HIERARCHY_LEVEL_COALITION = 4;
  // Values 5 and 6 are intentionally reserved (formerly BRIGADE, DIVISION).
  // Future tier-5+ support comes through ADR-067 parametric N-tier model,
  // not by re-introducing values here.
  reserved 5, 6;
}

// Device identity information
message DeviceIdentity {
  // Unique device identifier (hex-encoded, 32 chars from SHA-256 of public key)
  string device_id = 1;

  // Ed25519 public key (32 bytes)
  bytes public_key = 2;

  // Device type classification
  DeviceType device_type = 3;

  // Organization unit level
  HierarchyLevel organization_level = 4;

  // Organization unit identifier (deployment-defined; opaque to peat)
  string organization_unit_id = 5;

  // Common name / call sign
  string common_name = 6;

  // X.509 certificate chain (DER-encoded, leaf first) - optional for MVP
  repeated bytes certificates = 7;
}

// Authentication challenge for peer verification
message Challenge {
  // Random nonce (32 bytes) for replay protection
  bytes nonce = 1;

  // Challenge creation timestamp
  cap.common.v1.Timestamp timestamp = 2;

  // Challenger's device ID (hex string)
  string challenger_id = 3;

  // Challenge expiration timestamp
  cap.common.v1.Timestamp expires_at = 4;

  // Protocol version the challenger speaks (ADR-065).
  // `0` = pre-rc.21+1 peer (no version field on the wire, prost
  // default). The responder negotiates by picking
  // `min(challenge.protocol_version, CURRENT_PROTOCOL_VERSION)` and
  // signs the byte construction associated with that version. See
  // `SignedChallengeResponse.signature` for the per-version byte
  // string and ADR-065 for the full negotiation rule.
  uint32 protocol_version = 5;

  // Capability strings the challenger advertises (ADR-065).
  // Sorted, opaque ASCII identifiers. At protocol version 1 this is
  // **advertised-but-not-signed** — consumers driving feature-flagged
  // behaviour (e.g. "this peer supports the new cell-formation flow")
  // can read this field and adjust soft policy, but the field is not
  // part of the signed-message byte construction. A future v2
  // revision may extend the signed bytes to cover capabilities; see
  // ADR-065 "Capabilities (v1 semantics)" for the rationale.
  repeated string capabilities = 6;
}

// Signed response to authentication challenge
message SignedChallengeResponse {
  // Original challenge nonce (for correlation)
  bytes challenge_nonce = 1;

  // Responder's Ed25519 public key (32 bytes)
  bytes public_key = 2;

  // Ed25519 signature (64 bytes). The signed byte string depends on
  // the negotiated protocol version (ADR-065):
  //
  // **Version 0** (pre-rc.21+1 peers; `protocol_version` absent on
  // the wire and reads as `0` via the prost default):
  //
  //   signed = challenge.nonce
  //         || challenge.challenger_id
  //         || response.timestamp.seconds  (u64 little-endian, 8 bytes)
  //
  // **Version 1** (rc.21+1+; `CURRENT_PROTOCOL_VERSION` at this release):
  //
  //   signed = challenge.nonce
  //         || challenge.challenger_id
  //         || response.timestamp.seconds  (u64 little-endian, 8 bytes)
  //         || response.protocol_version   (u32 little-endian, 4 bytes)
  //
  // The `response.timestamp.seconds` value is the seconds-precision
  // wall-clock time the responder captured at sign time, the same
  // value carried in the `timestamp` field below — NOT the
  // challenger's `Challenge.timestamp`. Both signer and verifier
  // reconstruct the byte string from `response.timestamp.seconds`
  // (and, from v1 onward, `response.protocol_version`), so the
  // signature verifies independent of how long the auth flow takes
  // or whether it spans a wall-clock second boundary.
  //
  // Negotiation: the responder picks
  //   negotiated = min(challenge.protocol_version, CURRENT_PROTOCOL_VERSION)
  // sets `response.protocol_version = negotiated`, and uses the
  // negotiated version's byte construction. The verifier reads
  // `response.protocol_version` to know which construction to use.
  // A `response.protocol_version` value greater than the verifier's
  // `CURRENT_PROTOCOL_VERSION` surfaces as
  // `SecurityError::IncompatibleProtocolVersion`, distinct from
  // `InvalidSignature`. Full negotiation rules and rollout
  // semantics: ADR-065.
  bytes signature = 3;

  // Response creation timestamp. The `seconds` field is included
  // verbatim in the signed-message construction above; the `nanos`
  // field is informational only and is NOT covered by the signature.
  cap.common.v1.Timestamp timestamp = 4;

  // Responder's device type
  DeviceType device_type = 5;

  // X.509 certificate chain (DER-encoded) - optional for MVP
  repeated bytes certificates = 6;

  // Negotiated protocol version covered by the signature (ADR-065).
  // Set by the responder to
  //   min(challenge.protocol_version, CURRENT_PROTOCOL_VERSION).
  // From version 1 onward this value is included in the signed
  // byte string (u32 little-endian, 4 bytes), so a MITM cannot
  // strip the field or change its value without invalidating the
  // signature. A `0` here means the responder negotiated down to
  // the pre-version-token construction (the peer didn't advertise
  // a version, defaulting to 0).
  uint32 protocol_version = 7;

  // Capability strings the responder advertises (ADR-065). Same
  // v1 semantics as `Challenge.capabilities` —
  // advertised-but-not-signed, available to consumers for soft-
  // policy feature flagging.
  repeated string capabilities = 8;
}

// Signed beacon wrapper for discovery phase security
message SignedBeacon {
  // The beacon data (serialized beacon protobuf)
  bytes beacon_data = 1;

  // Device ID of signer (hex string)
  string signer_device_id = 2;

  // Ed25519 signature over beacon_data
  bytes signature = 3;

  // Signer's certificate (included periodically, not every beacon)
  optional bytes signer_certificate = 4;
}

// Security error details for protocol messages
message SecurityError {
  // Error code (e.g., "CERT_EXPIRED", "INVALID_SIGNATURE")
  string code = 1;

  // Human-readable error message
  string message = 2;

  // Device ID involved (if applicable)
  optional string device_id = 3;
}
