Skip to main content

secure_gate/traits/
reveal_secret.rs

1//! Traits for controlled, polymorphic secret revelation.
2//!
3//! This module defines the core traits that enforce explicit, auditable access to
4//! secret data across all wrapper types (`Fixed<T>`, `Dynamic<T>`, aliases, etc.).
5//!
6//! The design ensures:
7//! - No implicit borrowing (`Deref`, `AsRef`, etc.)
8//! - Scoped access is preferred (minimizes lifetime of exposed references)
9//! - Direct exposure is possible but clearly marked as an escape hatch
10//! - Metadata (`len`, `is_empty`) is always available without full exposure
11//!
12//! # Key Traits
13//!
14//! | Trait                  | Access     | Preferred Method          | Escape Hatch             | Metadata          | Feature     |
15//! |------------------------|------------|---------------------------|--------------------------|-------------------|-------------|
16//! | [`RevealSecret`]                | Read-only  | `with_secret` (scoped)    | `expose_secret`     | `len`, `is_empty` | Always |
17//! | [`crate::RevealSecretMut`]      | Mutable    | `with_secret_mut` (scoped)| `expose_secret_mut` | Inherits above    | Always |
18//!
19//! # Security Model
20//!
21//! - **Core wrappers** (`Fixed<T>`, `Dynamic<T>`) implement both traits → full access.
22//! - **Read-only wrappers** (encoding wrappers, random types) implement only `RevealSecret` → mutation prevented.
23//! - **Zero-cost** — all methods are `#[inline(always)]` where possible.
24//! - **Scoped access preferred** — `with_secret` / `with_secret_mut` limit borrow lifetime, reducing leak risk.
25//! - **Direct exposure** (`expose_secret` / `expose_secret_mut`) is provided for legitimate needs (FFI, third-party APIs), but marked as an escape hatch.
26//!
27//! # Usage Guidelines
28//!
29//! The preferred and recommended way to access secrets is the scoped `with_secret` /
30//! `with_secret_mut` methods. `expose_secret` / `expose_secret_mut` are escape hatches
31//! for rare cases and should be audited closely.
32//!
33//! - **Always prefer scoped methods** (`with_secret`, `with_secret_mut`) in application code.
34//! - Use direct exposure only when necessary (e.g., passing raw pointer + length to C FFI).
35//! - Audit every `expose_secret*` call — they should be rare and well-justified.
36//!
37//! # Examples
38//!
39//! Scoped (recommended):
40//!
41//! ```rust
42//! use secure_gate::{Fixed, RevealSecret};
43//!
44//! let secret = Fixed::new([42u8; 4]);
45//! let sum: u32 = secret.with_secret(|bytes| bytes.iter().map(|&b| b as u32).sum());
46//! assert_eq!(sum, 42 * 4);
47//! ```
48//!
49//! Direct (escape hatch – use with caution):
50//!
51//! ```rust
52//! use secure_gate::{Fixed, RevealSecret};
53//!
54//! let secret = Fixed::new([42u8; 4]);
55//!
56//! // Example: FFI call needing raw pointer + length
57//! // unsafe {
58//! //     c_function(secret.expose_secret().as_ptr(), secret.len());
59//! // }
60//! ```
61//!
62//! Mutable scoped:
63//!
64//! ```rust
65//! use secure_gate::{Fixed, RevealSecret, RevealSecretMut};
66//!
67//! let mut secret = Fixed::new([0u8; 4]);
68//! secret.with_secret_mut(|bytes| bytes[0] = 99);
69//! assert_eq!(secret.expose_secret()[0], 99);
70//! ```
71//!
72//! Polymorphic generic code:
73//!
74//! ```rust
75//! use secure_gate::RevealSecret;
76//!
77//! fn print_length<S: RevealSecret>(secret: &S) {
78//!     println!("Length: {} bytes", secret.len());
79//! }
80//! ```
81//!
82//! These traits are the foundation of secure-gate's security model: all secret access is
83//! explicit, auditable, and controlled. Prefer scoped methods in nearly all cases.
84//!
85//! # Implementation Notes
86//!
87//! Long-lived `expose_secret()` references can defeat scoping — the borrow outlives the
88//! call site and the compiler cannot enforce that the secret is not retained. This is an
89//! intentional escape hatch for FFI and legacy APIs; audit every call site.
90pub trait RevealSecret {
91    /// The inner secret type being revealed.
92    ///
93    /// This can be a sized type (e.g. `[u8; N]`, `u32`) or unsized (e.g. `str`, `[u8]`).
94    type Inner: ?Sized;
95
96    /// Provides scoped (recommended) read-only access to the secret.
97    ///
98    /// The closure receives a reference that cannot escape — the borrow ends when
99    /// the closure returns, minimizing the lifetime of the exposed secret.
100    /// Prefer this over [`expose_secret`](Self::expose_secret) in all application code.
101    ///
102    /// # Examples
103    ///
104    /// ```rust
105    /// use secure_gate::{Fixed, RevealSecret};
106    ///
107    /// let secret = Fixed::new([42u8; 4]);
108    /// let sum: u32 = secret.with_secret(|bytes| bytes.iter().map(|&b| b as u32).sum());
109    /// assert_eq!(sum, 42 * 4);
110    /// ```
111    fn with_secret<F, R>(&self, f: F) -> R
112    where
113        F: FnOnce(&Self::Inner) -> R;
114
115    /// Returns a direct (auditable) read-only reference to the secret.
116    ///
117    /// Long-lived `expose_secret()` references can defeat scoping — prefer
118    /// [`with_secret`](Self::with_secret) in application code. Use this only when
119    /// a long-lived reference is unavoidable (e.g. FFI, third-party APIs).
120    ///
121    /// # Examples
122    ///
123    /// ```rust
124    /// use secure_gate::{Fixed, RevealSecret};
125    ///
126    /// let secret = Fixed::new([42u8; 4]);
127    ///
128    /// // Auditable escape hatch — FFI use case:
129    /// // unsafe { c_fn(secret.expose_secret().as_ptr(), secret.len()); }
130    /// let _ = secret.expose_secret();
131    /// ```
132    fn expose_secret(&self) -> &Self::Inner;
133
134    /// Returns the length of the secret in bytes.
135    ///
136    /// Always safe to call — does not expose secret contents.
137    fn len(&self) -> usize;
138
139    /// Returns `true` if the secret is empty.
140    ///
141    /// Always safe to call — does not expose secret contents.
142    #[inline(always)]
143    fn is_empty(&self) -> bool {
144        self.len() == 0
145    }
146}