Skip to main content

aura_guards/guards/
mod.rs

1#![allow(
2    missing_docs,
3    unused_variables,
4    clippy::unwrap_used,
5    clippy::expect_used,
6    dead_code,
7    clippy::match_like_matches_macro,
8    clippy::type_complexity,
9    clippy::while_let_loop,
10    clippy::redundant_closure,
11    clippy::large_enum_variant,
12    clippy::unused_unit,
13    clippy::get_first,
14    clippy::single_range_in_vec_init,
15    clippy::disallowed_methods, // Guard chain coordinates time/random effects
16    deprecated // Deprecated time/random functions used intentionally for effect coordination
17)]
18//! Layer 4: Protocol Guard Chain - Authorization, Budget, Privacy, Journal
19//!
20//! Choreographic enforcement of authorization, flow budgets, privacy, and journal consistency
21//! through a composable guard chain applied to **every send** (per docs/003_information_flow_contract.md).
22//!
23//! # Choreography-First Architecture
24//!
25//! Guard effects can originate from two sources that use the same `EffectCommand` system:
26//!
27//! 1. **Choreographic Annotations** (compile-time): The `tell!` macro generates
28//!    `EffectCommand` sequences from annotations like `guard_capability`, `flow_cost`,
29//!    `journal_facts`, and `leak`. These are produced by `aura_macros::tell`
30//!    via the generated `effect_bridge::annotation_to_commands()` function.
31//!
32//! 2. **Runtime Guard Chain** (send-site): The `GuardChain::standard()` evaluates
33//!    pure guards (`CapabilityGuard`, `FlowBudgetGuard`, `JournalCouplingGuard`,
34//!    `LeakageTrackingGuard`) against a `GuardSnapshot` and produces `EffectCommand`
35//!    sequences at each protocol send site.
36//!
37//! Both sources produce `Vec<EffectCommand>` that are executed through an `EffectInterpreter`:
38//! - Production: `ProductionEffectInterpreter` (aura-effects)
39//! - Simulation: `SimulationEffectInterpreter` (aura-simulator)
40//! - Testing: `BorrowedEffectInterpreter` / mock interpreters
41//!
42//! # Guard Chain Sequence
43//!
44//! `CapGuard` → `FlowGuard` → `JournalCoupler` → `LeakageTracker` → `Transport`
45//!
46//! # Invariant: Charge-Before-Send
47//!
48//! No transport side effect occurs unless all preceding guards succeed. Ensures:
49//! - Authorization verified (Biscuit tokens evaluated)
50//! - Flow budgets charged (atomically incremented spent counter)
51//! - Delta facts committed to journal (atomic with send)
52//! - Leakage budget validated (per observer class)
53//!
54//! # Receipt Semantics
55//!
56//! (per docs/003_information_flow_contract.md §3.2)
57//! FlowGuard produces receipts proving budget charges, scoped to (ContextId, peer) pairs,
58//! bound to Epochs (budget rotation). Required for relayed messages (per-hop forwarding).
59//!
60//! # Guard Implementations
61//!
62//! - **CapGuard**: Biscuit token evaluation with `need(σ) ≤ C` predicate
63//! - **FlowGuard**: Flow budget enforcement, atomically increments spent counter
64//! - **JournalCoupler**: Merges delta facts atomic with send
65//! - **LeakageTracker**: Privacy budget per observer class (external, neighbor, group)
66//!
67//! # Example: Using Guard Chain with Effect Interpreter
68//!
69//! ```rust,ignore
70//! use aura_guards::{create_send_guard, SendGuardChain};
71//!
72//! let guard = create_send_guard(
73//!     aura_core::capability_name!("chat:message:send"),
74//!     context_id,
75//!     peer_device,
76//!     100, // flow cost
77//! );
78//!
79//! let result = send_guard.evaluate(&effect_system).await?;
80//! if result.authorized {
81//!     // Proceed with send using receipt for anti-replay protection
82//!     transport.send_with_receipt(message, result.receipt.unwrap()).await?;
83//! }
84//! ```
85//!
86//! ### Advanced Protocol Guards
87//! ```rust,ignore
88//! use crate::guards::{ProtocolGuard, GuardedExecution};
89//!
90//! // For development/testing, use deterministic test keys:
91//! let guard = ProtocolGuard::new_for_testing("complex_operation")
92//!     .delta_facts(vec![fact1, fact2])
93//!     .leakage_budget(LeakageBudget::new(1, 2, 0));
94//!
95//! // For production, use real keys:
96//! // let guard = ProtocolGuard::new(root_public_key, authority_id, "complex_operation")
97//! //     .require_token(biscuit_token)
98//! //     .delta_facts(vec![fact1, fact2])
99//! //     .leakage_budget(LeakageBudget::new(1, 2, 0));
100//!
101//! // Execute with guards
102//! let result = guard.execute_with_effects(effect_system, |effects| async move {
103//!     // Protocol execution here, using `effects` if needed
104//!     Ok(protocol_result)
105//! }).await?;
106//! ```
107
108// Guard implementations
109pub mod chain; // SendGuardChain (guard chain orchestration)
110pub mod config;
111pub mod deltas;
112pub mod execution;
113pub mod executor;
114pub mod flow;
115pub mod journal; // JournalCoupler
116pub mod policy; // Effect policy guards
117pub mod privacy;
118pub mod pure;
119pub mod traits; // GuardContextProvider
120pub mod types; // Shared guard types
121
122// Biscuit-based capability guards
123pub mod biscuit_evaluator;
124pub mod capability_guard;
125
126// Core re-exports
127pub use aura_core::effects::{FlowBudgetEffects, FlowHint};
128pub use chain::{create_send_guard, create_send_guard_op, SendGuardChain, SendGuardResult};
129pub use flow::FlowGuard;
130pub use journal::{
131    CouplingMetrics, JournalCoupler, JournalCouplerBuilder, JournalCouplingResult, JournalOperation,
132};
133pub use traits::GuardContextProvider;
134pub use types::{CapabilityId, GuardOperation, GuardOperationId};
135
136use aura_core::effects::{
137    AuthorizationEffects, JournalEffects, LeakageEffects, PhysicalTimeEffects, RandomEffects,
138    StorageEffects,
139};
140use aura_core::AuraResult;
141use aura_core::AuthorityId;
142use biscuit_auth::{Biscuit, PublicKey};
143use std::future::Future;
144
145/// Composite effect requirements for guard evaluation/execution.
146pub trait GuardEffects:
147    JournalEffects
148    + StorageEffects
149    + FlowBudgetEffects
150    + PhysicalTimeEffects
151    + RandomEffects
152    + AuthorizationEffects
153    + LeakageEffects
154    + Send
155    + Sync
156{
157}
158
159impl<T> GuardEffects for T where
160    T: JournalEffects
161        + StorageEffects
162        + FlowBudgetEffects
163        + PhysicalTimeEffects
164        + RandomEffects
165        + AuthorizationEffects
166        + LeakageEffects
167        + Send
168        + Sync
169{
170}
171
172/// Protocol execution guard combining authorization checking, delta application, and privacy tracking
173#[derive(Debug, Clone)]
174pub struct ProtocolGuard {
175    /// Root public key for Biscuit token verification
176    pub root_public_key: PublicKey,
177    /// Authority ID for this guard context
178    pub authority_id: AuthorityId,
179    /// Context ID for leakage accounting
180    pub context_id: aura_core::ContextId,
181    /// Observer classes that can see this operation
182    pub observable_by: Vec<AdversaryClass>,
183    /// Required Biscuit authorization tokens for this operation
184    pub required_tokens: Vec<Biscuit>,
185    /// Facts to be merged into the journal after successful execution
186    pub delta_facts: Vec<serde_json::Value>, // JSON-encoded facts until typed fact system
187    /// Privacy leakage budget for this operation
188    pub leakage_budget: LeakageBudget,
189    /// Operation identifier for logging and metrics
190    pub operation_id: GuardOperationId,
191}
192
193/// Privacy leakage budget tracking across adversary classes
194#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
195pub struct LeakageBudget {
196    /// External adversary leakage (bits)
197    pub external: u32,
198    /// Neighbor adversary leakage (bits)
199    pub neighbor: u32,
200    /// In-group adversary leakage (bits)
201    pub in_group: u32,
202}
203
204/// Result of guarded protocol execution
205#[derive(Debug)]
206pub struct GuardedExecutionResult<T> {
207    /// The protocol execution result
208    pub result: T,
209    /// Whether all guards passed
210    pub guards_passed: bool,
211    /// Applied delta facts
212    pub applied_deltas: Vec<serde_json::Value>,
213    /// Consumed leakage budget
214    pub consumed_budget: LeakageBudget,
215    /// Execution metrics
216    pub metrics: ExecutionMetrics,
217}
218
219/// Metrics for protocol execution
220#[derive(Debug, Default)]
221pub struct ExecutionMetrics {
222    /// Guard evaluation time (microseconds)
223    pub guard_eval_time_us: u64,
224    /// Delta application time (microseconds)
225    pub delta_apply_time_us: u64,
226    /// Total execution time (microseconds)
227    pub total_execution_time_us: u64,
228    /// Number of authorization checks performed
229    pub authorization_checks: u32,
230    /// Number of facts applied
231    pub facts_applied: u32,
232}
233
234impl ProtocolGuard {
235    /// Create a new protocol guard with required key material
236    ///
237    /// # Arguments
238    /// * `root_public_key` - The Biscuit root public key for token verification
239    /// * `authority_id` - The authority ID for this guard context
240    /// * `operation_id` - Identifier for logging and metrics
241    pub fn new(
242        root_public_key: PublicKey,
243        authority_id: AuthorityId,
244        operation_id: impl Into<GuardOperationId>,
245    ) -> Self {
246        Self {
247            root_public_key,
248            authority_id,
249            context_id: aura_core::ContextId::new_from_entropy([2u8; 32]),
250            observable_by: vec![
251                AdversaryClass::External,
252                AdversaryClass::Neighbor,
253                AdversaryClass::InGroup,
254            ],
255            required_tokens: Vec::new(),
256            delta_facts: Vec::new(),
257            leakage_budget: LeakageBudget::zero(),
258            operation_id: operation_id.into(),
259        }
260    }
261
262    /// Add a required authorization token to this guard
263    pub fn require_token(mut self, token: Biscuit) -> Self {
264        self.required_tokens.push(token);
265        self
266    }
267
268    /// Add multiple required authorization tokens to this guard
269    pub fn require_tokens(mut self, tokens: Vec<Biscuit>) -> Self {
270        self.required_tokens.extend(tokens);
271        self
272    }
273
274    /// Add delta facts to be applied after successful execution
275    pub fn delta_facts(mut self, facts: Vec<serde_json::Value>) -> Self {
276        self.delta_facts = facts;
277        self
278    }
279
280    /// Set the leakage budget for this operation
281    pub fn leakage_budget(mut self, budget: LeakageBudget) -> Self {
282        self.leakage_budget = budget;
283        self
284    }
285
286    /// Execute a protocol operation with full guard enforcement
287    pub async fn execute_with_effects<E, T, F, Fut>(
288        &self,
289        effect_system: &mut E,
290        operation: F,
291    ) -> AuraResult<GuardedExecutionResult<T>>
292    where
293        E: GuardEffects + aura_core::TimeEffects + traits::GuardContextProvider,
294        F: FnOnce(&mut E) -> Fut,
295        Fut: Future<Output = AuraResult<T>>,
296    {
297        execution::execute_guarded_operation(self, effect_system, operation).await
298    }
299
300    /// Create a protocol guard with deterministic test keys for development/testing
301    ///
302    /// Uses a deterministic keypair and authority ID. This is useful for:
303    /// - Development and testing where real keys aren't available
304    /// - Macro-generated guards that don't have key context
305    /// - Scenarios where guard evaluation is bypassed or mocked
306    ///
307    /// # Security Warning
308    /// Production code should use `new()` with real key material from the
309    /// authority's Biscuit root key and actual AuthorityId.
310    pub fn new_for_testing(operation_id: impl Into<GuardOperationId>) -> Self {
311        // Use deterministic seed for reproducible behavior
312        let keypair = biscuit_auth::KeyPair::new();
313        let authority_id = AuthorityId::new_from_entropy([0u8; 32]);
314
315        Self {
316            root_public_key: keypair.public(),
317            authority_id,
318            context_id: aura_core::ContextId::new_from_entropy([2u8; 32]),
319            observable_by: vec![
320                AdversaryClass::External,
321                AdversaryClass::Neighbor,
322                AdversaryClass::InGroup,
323            ],
324            required_tokens: Vec::new(),
325            delta_facts: Vec::new(),
326            leakage_budget: LeakageBudget::zero(),
327            operation_id: operation_id.into(),
328        }
329    }
330
331    /// Set the context ID for leakage accounting
332    pub fn context_id(mut self, context_id: aura_core::ContextId) -> Self {
333        self.context_id = context_id;
334        self
335    }
336
337    /// Set explicit observer classes for leakage accounting
338    pub fn observable_by(mut self, observers: Vec<AdversaryClass>) -> Self {
339        self.observable_by = observers;
340        self
341    }
342}
343
344impl LeakageBudget {
345    /// Create a new leakage budget
346    pub fn new(external: u32, neighbor: u32, in_group: u32) -> Self {
347        Self {
348            external,
349            neighbor,
350            in_group,
351        }
352    }
353
354    /// Create a zero leakage budget (no privacy cost)
355    pub fn zero() -> Self {
356        Self::new(0, 0, 0)
357    }
358
359    /// Check if this budget is within the allowed limits
360    pub fn is_within_limits(&self, limits: &LeakageBudget) -> bool {
361        self.external <= limits.external
362            && self.neighbor <= limits.neighbor
363            && self.in_group <= limits.in_group
364    }
365
366    /// Add two budgets together
367    pub fn add(&self, other: &LeakageBudget) -> Self {
368        Self {
369            external: self.external + other.external,
370            neighbor: self.neighbor + other.neighbor,
371            in_group: self.in_group + other.in_group,
372        }
373    }
374}
375
376/// Convenience macro for creating protocol guards with test keys
377///
378/// For production use with real keys, use `ProtocolGuard::new()` directly.
379#[macro_export]
380macro_rules! guard {
381    (
382        operation: $op:expr,
383        $(deltas: [$($delta:expr),*],)?
384        $(leakage: ($ext:expr, $ngh:expr, $grp:expr),)?
385    ) => {
386        {
387            let mut guard = $crate::guards::ProtocolGuard::new_for_testing($op);
388
389            $(
390                guard = guard.delta_facts(vec![$($delta),*]);
391            )?
392
393            $(
394                guard = guard.leakage_budget($crate::guards::LeakageBudget::new($ext, $ngh, $grp));
395            )?
396
397            guard
398        }
399    };
400}
401
402// Re-export submodules
403pub use deltas::*;
404pub use execution::*;
405pub use privacy::*;
406
407// Re-export Biscuit guard types
408pub use biscuit_evaluator::{BiscuitGuardEvaluator, GuardError, GuardResult};
409pub use capability_guard::{CapabilityGuard, CapabilityGuardExt};
410
411// Re-export policy guard types
412pub use policy::{EffectPolicyError, EffectPolicyExt, EffectPolicyGuard, EffectPolicyResult};
413
414// Re-export executor functions for choreography integration
415pub use executor::{
416    execute_effect_commands, execute_guard_plan, execute_guarded_choreography,
417    BorrowedEffectInterpreter, ChoreographyCommand, ChoreographyResult, EffectSystemInterpreter,
418    GuardChainExecutor, GuardChainResult, GuardPlan,
419};