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};