Skip to main content

cpop_protocol/rfc/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! RFC-compliant data structures for Proof-of-Process evidence.
4//!
5//! This module implements the CDDL-defined structures from draft-condrey-rats-pop-01
6//! and draft-condrey-rats-pop-schema-01. All structures support both CBOR and JSON
7//! serialization for backwards compatibility.
8
9use serde::{Deserialize, Serialize};
10use subtle::ConstantTimeEq;
11
12pub mod biology;
13pub mod checkpoint;
14pub mod fixed_point;
15pub mod jitter_binding;
16pub mod packet;
17pub(crate) mod serde_helpers;
18pub mod time_evidence;
19pub mod vdf;
20pub mod wire_types;
21
22pub use biology::{
23    BiologyInvariantClaim, BiologyMeasurements, BiologyScoringParameters, ValidationStatus,
24};
25pub use checkpoint::{BioBinding, CheckpointRfc, SaVdfProof};
26pub use fixed_point::{
27    Centibits, DeciWpm, Decibits, Microdollars, Millibits, RhoMillibits, SlopeDecibits,
28};
29pub use jitter_binding::{
30    ActiveProbes, BindingMac, EntropyCommitment, GaltonInvariant, JitterBinding, JitterSummary,
31    LabyrinthStructure, ReflexGate,
32};
33pub use packet::{
34    ContentHashTree, CorrelationProof, EnclaveVise, ErrorTopology, JitterSealStructure,
35    KeyRotationMetadata, PacketRfc, PrivacyBudgetCertificate, ProfileDeclaration, VdfStructure,
36    ZkProcessVerdict,
37};
38pub use time_evidence::{
39    BlockchainAnchor, RoughtimeSample, TimeBindingTier, TimeEvidence, TsaResponse,
40};
41pub use vdf::{CalibrationAttestation, VdfProofRfc};
42pub use wire_types::{AttestationResultWire, CheckpointWire, EvidencePacketWire};
43
44/// CBOR tag for CPoP evidence packets (0x43504F50 = "CPOP").
45pub const CBOR_TAG_EVIDENCE_PACKET: u64 = 1129336656;
46/// CBOR tag for attestation results (0x43574152 = "CWAR").
47pub const CBOR_TAG_ATTESTATION_RESULT: u64 = 1129791826;
48
49/// IANA Private Enterprise Number for CPOP Inc.
50/// Registered under SMI Network Management Private Enterprise Codes.
51pub const IANA_PEN: u32 = 65074;
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
54#[repr(u64)]
55pub enum HashAlgorithm {
56    /// SHA-256 (32-byte digest).
57    Sha256 = 1,
58    /// SHA-384 (48-byte digest).
59    Sha384 = 2,
60    /// SHA-512 (64-byte digest).
61    Sha512 = 3,
62}
63
64/// Hardware attestation strength tier per draft-condrey-rats-pop.
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
66#[repr(u64)]
67pub enum AttestationTier {
68    /// Pure software signing, no hardware root of trust.
69    SoftwareOnly = 1,
70    /// Software key with remote attestation evidence.
71    AttestedSoftware = 2,
72    /// Key bound to TPM/Secure Enclave.
73    HardwareBound = 3,
74    /// Hardware-hardened with anti-tamper protections.
75    HardwareHardened = 4,
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
79#[repr(u64)]
80pub enum ContentTier {
81    /// Minimal evidence (checkpoints and hashes only).
82    Core = 1,
83    /// Additional behavioral metrics included.
84    Enhanced = 2,
85    /// Full forensic payload with jitter and HID data.
86    Maximum = 3,
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
90#[repr(u64)]
91pub enum ProofAlgorithm {
92    SwfSha256 = 10,
93    SwfArgon2id = 20,
94    /// Argon2id with cross-checkpoint entanglement.
95    SwfArgon2idEntangled = 21,
96}
97
98#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
99#[repr(u64)]
100pub enum Verdict {
101    /// Evidence verified as authentic human authorship.
102    Authentic = 1,
103    /// Insufficient data to reach a definitive conclusion.
104    Inconclusive = 2,
105    /// Anomalies detected; manual review recommended.
106    Suspicious = 3,
107    /// Evidence is structurally or cryptographically invalid.
108    Invalid = 4,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct HashValue {
113    #[serde(rename = "1")]
114    pub algorithm: HashAlgorithm,
115    #[serde(rename = "2", with = "serde_bytes")]
116    pub digest: Vec<u8>,
117}
118
119impl HashValue {
120    /// Constant-time comparison to prevent timing side-channels on HMAC outputs.
121    pub fn ct_eq(&self, other: &Self) -> bool {
122        self.algorithm == other.algorithm && self.digest.ct_eq(&other.digest).into()
123    }
124
125    pub fn expected_digest_len(&self) -> usize {
126        match self.algorithm {
127            HashAlgorithm::Sha256 => 32,
128            HashAlgorithm::Sha384 => 48,
129            HashAlgorithm::Sha512 => 64,
130        }
131    }
132
133    pub fn validate(&self) -> bool {
134        self.digest.len() == self.expected_digest_len()
135    }
136}
137
138// PartialEq/Eq for non-security-critical uses (serialization, tests).
139// Security-critical verification must use ct_eq().
140impl PartialEq for HashValue {
141    fn eq(&self, other: &Self) -> bool {
142        self.algorithm == other.algorithm && self.digest == other.digest
143    }
144}
145
146impl Eq for HashValue {}
147
148#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct DocumentRef {
150    #[serde(rename = "1")]
151    pub content_hash: HashValue,
152    #[serde(rename = "2", skip_serializing_if = "Option::is_none")]
153    pub filename: Option<String>,
154    #[serde(rename = "3")]
155    pub byte_length: u64,
156    #[serde(rename = "4")]
157    pub char_count: u64,
158}
159
160/// Single checkpoint in the causality chain, binding content state to a timestamp.
161#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct Checkpoint {
163    #[serde(rename = "1")]
164    pub sequence: u64,
165    #[serde(rename = "2", with = "serde_bytes")]
166    pub checkpoint_id: Vec<u8>,
167    #[serde(rename = "3")]
168    pub timestamp: u64,
169    #[serde(rename = "4")]
170    pub content_hash: HashValue,
171    #[serde(rename = "5")]
172    pub char_count: u64,
173    #[serde(rename = "7")]
174    pub prev_hash: HashValue,
175    #[serde(rename = "8")]
176    pub checkpoint_hash: HashValue,
177    #[serde(rename = "9", skip_serializing_if = "Option::is_none")]
178    pub jitter_hash: Option<HashValue>,
179}
180
181#[derive(Debug, Clone, Serialize, Deserialize)]
182pub struct EvidencePacket {
183    #[serde(rename = "1")]
184    pub version: u32,
185    #[serde(rename = "2")]
186    pub profile_uri: String,
187    #[serde(rename = "3", with = "serde_bytes")]
188    pub packet_id: Vec<u8>,
189    #[serde(rename = "4")]
190    pub created: u64,
191    #[serde(rename = "5")]
192    pub document: DocumentRef,
193    #[serde(rename = "6")]
194    pub checkpoints: Vec<Checkpoint>,
195    #[serde(rename = "7", skip_serializing_if = "Option::is_none")]
196    pub attestation_tier: Option<AttestationTier>,
197    #[serde(rename = "19", skip_serializing_if = "Option::is_none")]
198    pub baseline_verification: Option<crate::baseline::BaselineVerification>,
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize)]
202pub struct AttestationResult {
203    #[serde(rename = "1")]
204    pub version: u32,
205    #[serde(rename = "2")]
206    pub evidence_ref: HashValue,
207    #[serde(rename = "3")]
208    pub verdict: Verdict,
209    #[serde(rename = "4")]
210    pub attestation_tier: AttestationTier,
211    #[serde(rename = "5")]
212    pub chain_length: u64,
213    #[serde(rename = "6")]
214    pub chain_duration: u64,
215    #[serde(rename = "12")]
216    pub created: u64,
217    /// None if no baseline verification was present in the evidence.
218    #[serde(rename = "14", skip_serializing_if = "Option::is_none")]
219    pub confidence_tier: Option<crate::baseline::ConfidenceTier>,
220}