Skip to main content

cpop_protocol/
rfc.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use serde::{Deserialize, Serialize};
4use subtle::ConstantTimeEq;
5
6/// CBOR tag for CPoP evidence packets (0x43504F50 = "CPOP").
7pub const CBOR_TAG_EVIDENCE_PACKET: u64 = 1129336656;
8/// CBOR tag for attestation results (0x43574152 = "CWAR").
9pub const CBOR_TAG_ATTESTATION_RESULT: u64 = 1129791826;
10
11/// IANA Private Enterprise Number for CPOP Inc.
12/// Registered under SMI Network Management Private Enterprise Codes.
13pub const IANA_PEN: u32 = 65074;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
16#[repr(u64)]
17pub enum HashAlgorithm {
18    /// SHA-256 (32-byte digest).
19    Sha256 = 1,
20    /// SHA-384 (48-byte digest).
21    Sha384 = 2,
22    /// SHA-512 (64-byte digest).
23    Sha512 = 3,
24}
25
26/// Hardware attestation strength tier per draft-condrey-rats-pop.
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
28#[repr(u64)]
29pub enum AttestationTier {
30    /// Pure software signing, no hardware root of trust.
31    SoftwareOnly = 1,
32    /// Software key with remote attestation evidence.
33    AttestedSoftware = 2,
34    /// Key bound to TPM/Secure Enclave.
35    HardwareBound = 3,
36    /// Hardware-hardened with anti-tamper protections.
37    HardwareHardened = 4,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
41#[repr(u64)]
42pub enum ContentTier {
43    /// Minimal evidence (checkpoints and hashes only).
44    Core = 1,
45    /// Additional behavioral metrics included.
46    Enhanced = 2,
47    /// Full forensic payload with jitter and HID data.
48    Maximum = 3,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
52#[repr(u64)]
53pub enum ProofAlgorithm {
54    SwfSha256 = 10,
55    SwfArgon2id = 20,
56    /// Argon2id with cross-checkpoint entanglement.
57    SwfArgon2idEntangled = 21,
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
61#[repr(u64)]
62pub enum Verdict {
63    /// Evidence verified as authentic human authorship.
64    Authentic = 1,
65    /// Insufficient data to reach a definitive conclusion.
66    Inconclusive = 2,
67    /// Anomalies detected; manual review recommended.
68    Suspicious = 3,
69    /// Evidence is structurally or cryptographically invalid.
70    Invalid = 4,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct HashValue {
75    #[serde(rename = "1")]
76    pub algorithm: HashAlgorithm,
77    #[serde(rename = "2", with = "serde_bytes")]
78    pub digest: Vec<u8>,
79}
80
81impl HashValue {
82    /// Constant-time comparison to prevent timing side-channels on HMAC outputs.
83    pub fn ct_eq(&self, other: &Self) -> bool {
84        self.algorithm == other.algorithm && self.digest.ct_eq(&other.digest).into()
85    }
86
87    pub fn expected_digest_len(&self) -> usize {
88        match self.algorithm {
89            HashAlgorithm::Sha256 => 32,
90            HashAlgorithm::Sha384 => 48,
91            HashAlgorithm::Sha512 => 64,
92        }
93    }
94
95    pub fn validate(&self) -> bool {
96        self.digest.len() == self.expected_digest_len()
97    }
98}
99
100// PartialEq/Eq for non-security-critical uses (serialization, tests).
101// Security-critical verification must use ct_eq().
102impl PartialEq for HashValue {
103    fn eq(&self, other: &Self) -> bool {
104        self.algorithm == other.algorithm && self.digest == other.digest
105    }
106}
107
108impl Eq for HashValue {}
109
110#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct DocumentRef {
112    #[serde(rename = "1")]
113    pub content_hash: HashValue,
114    #[serde(rename = "2", skip_serializing_if = "Option::is_none")]
115    pub filename: Option<String>,
116    #[serde(rename = "3")]
117    pub byte_length: u64,
118    #[serde(rename = "4")]
119    pub char_count: u64,
120}
121
122/// Single checkpoint in the causality chain, binding content state to a timestamp.
123#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct Checkpoint {
125    #[serde(rename = "1")]
126    pub sequence: u64,
127    #[serde(rename = "2", with = "serde_bytes")]
128    pub checkpoint_id: Vec<u8>,
129    #[serde(rename = "3")]
130    pub timestamp: u64,
131    #[serde(rename = "4")]
132    pub content_hash: HashValue,
133    #[serde(rename = "5")]
134    pub char_count: u64,
135    #[serde(rename = "7")]
136    pub prev_hash: HashValue,
137    #[serde(rename = "8")]
138    pub checkpoint_hash: HashValue,
139    #[serde(rename = "9", skip_serializing_if = "Option::is_none")]
140    pub jitter_hash: Option<HashValue>,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct EvidencePacket {
145    #[serde(rename = "1")]
146    pub version: u32,
147    #[serde(rename = "2")]
148    pub profile_uri: String,
149    #[serde(rename = "3", with = "serde_bytes")]
150    pub packet_id: Vec<u8>,
151    #[serde(rename = "4")]
152    pub created: u64,
153    #[serde(rename = "5")]
154    pub document: DocumentRef,
155    #[serde(rename = "6")]
156    pub checkpoints: Vec<Checkpoint>,
157    #[serde(rename = "7", skip_serializing_if = "Option::is_none")]
158    pub attestation_tier: Option<AttestationTier>,
159    #[serde(rename = "19", skip_serializing_if = "Option::is_none")]
160    pub baseline_verification: Option<crate::baseline::BaselineVerification>,
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct AttestationResult {
165    #[serde(rename = "1")]
166    pub version: u32,
167    #[serde(rename = "2")]
168    pub evidence_ref: HashValue,
169    #[serde(rename = "3")]
170    pub verdict: Verdict,
171    #[serde(rename = "4")]
172    pub attestation_tier: AttestationTier,
173    #[serde(rename = "5")]
174    pub chain_length: u64,
175    #[serde(rename = "6")]
176    pub chain_duration: u64,
177    #[serde(rename = "12")]
178    pub created: u64,
179    /// None if no baseline verification was present in the evidence.
180    #[serde(rename = "14", skip_serializing_if = "Option::is_none")]
181    pub confidence_tier: Option<crate::baseline::ConfidenceTier>,
182}