Skip to main content

base64_ng/v2/assurance/
provider.rs

1//! Unsafe protected-memory provider protocol.
2
3use super::{
4    AccountingPosture, AttestationEvidence, JournalDisposition, PhysicalProtection,
5    ProtectionError, ProtectionRequest, ProviderHealth, ProviderLimits, ProviderReport,
6    TeardownCursor, WipeEvidence,
7};
8
9/// Conclusive or indeterminate provider sub-operation result.
10#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
11#[non_exhaustive]
12pub enum ProviderOperationResult {
13    /// The operation conclusively applied.
14    Applied,
15    /// The operation conclusively did not apply.
16    NotApplied,
17    /// The provider cannot determine whether the operation applied.
18    Indeterminate,
19}
20
21impl ProviderOperationResult {
22    pub(crate) const fn journal_disposition(self) -> JournalDisposition {
23        match self {
24            Self::Applied => JournalDisposition::Applied,
25            Self::NotApplied => JournalDisposition::NotApplied,
26            Self::Indeterminate => JournalDisposition::Indeterminate,
27        }
28    }
29}
30
31/// Wipe confirmation returned after the crate overwrites the full range.
32#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
33pub struct WipeConfirmation {
34    /// Provider result for the modeled wipe/barrier operation.
35    pub result: ProviderOperationResult,
36    /// Strongest evidence independently established by the provider.
37    pub evidence: WipeEvidence,
38}
39
40/// Disposal result that never makes an ambiguous handle retryable.
41pub enum DisposalResult<Handle> {
42    /// Disposal completed and no allocation capability remains.
43    Applied,
44    /// Disposal conclusively did not apply; the handle remains live.
45    NotApplied(Handle),
46    /// Allocation presence is unknown and no addressable handle remains.
47    AllocationPresenceUnknown,
48}
49
50/// Provider-owned quarantine transfer record.
51#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
52pub struct QuarantineRecord {
53    /// Earliest teardown stage still required.
54    pub pending_stage: super::PendingStage,
55    /// Wipe evidence actually retained.
56    pub wipe: WipeEvidence,
57    /// Last honest physical-protection result.
58    pub physical_protection: PhysicalProtection,
59    /// Conservative accounting result.
60    pub accounting: AccountingPosture,
61    /// Monotonic volatile journal cursor.
62    pub cursor: TeardownCursor,
63    /// Retry attempt charged by this transfer.
64    pub retry_attempt: usize,
65}
66
67/// Crate-issued access capability for ownership-sensitive provider hooks.
68///
69/// The type is public only because it appears in the unsafe provider protocol.
70/// Its value cannot be constructed by external safe code, so a provider's raw
71/// allocation handles cannot bypass [`super::ProtectedSecret`].
72pub struct ProviderAccess {
73    _private: (),
74}
75
76impl ProviderAccess {
77    pub(crate) const fn new() -> Self {
78        Self { _private: () }
79    }
80}
81
82/// Protected allocation and bounded quarantine provider.
83///
84/// # Safety
85///
86/// An implementation must enforce unique live handles, stable backing ranges,
87/// checked finite admission budgets, generation/ABA safety, and infallible
88/// allocation-free quarantine transfer. Returned byte slices must name exactly
89/// one live handle's complete logical allocation. Every hook must be
90/// non-unwinding. Provider operations must obey their reported
91/// applied/not-applied/indeterminate disposition; non-idempotent ambiguous work
92/// must not be replayed. `AllocationPresenceUnknown` must destroy every pointer
93/// capability and retain only a non-owning tombstone identity.
94#[allow(unsafe_code)]
95pub unsafe trait ProtectedMemoryProvider {
96    /// Live allocation handle owned by one protected typestate value.
97    type Handle;
98    /// Pre-plaintext reservation carrying one registry slot and full budget.
99    type Reservation;
100
101    /// Stable opaque provider-instance identity.
102    fn provider_identity(&self) -> usize;
103
104    /// Provider-instance generation. Reconstruction must change it.
105    fn provider_generation(&self) -> usize;
106
107    /// Generation changed by provider health degradation.
108    fn health_generation(&self) -> usize;
109
110    /// Generation changed by physical protection transitions.
111    fn protection_generation(&self) -> usize;
112
113    /// Current provider health.
114    fn health(&self) -> ProviderHealth;
115
116    /// Finite provider limits.
117    fn limits(&self) -> ProviderLimits;
118
119    /// Redacted aggregate report.
120    fn report(&self) -> ProviderReport;
121
122    /// Reserves registry and complete resource budgets before allocation.
123    fn reserve(
124        &self,
125        access: &ProviderAccess,
126        request: ProtectionRequest,
127    ) -> Result<Self::Reservation, ProtectionError>;
128
129    /// Creates protected storage without plaintext and consumes the reservation.
130    fn materialize(
131        &self,
132        access: &ProviderAccess,
133        reservation: Self::Reservation,
134    ) -> Result<Self::Handle, ProtectionError>;
135
136    /// Complete logical allocation length.
137    fn logical_len(&self, access: &ProviderAccess, handle: &Self::Handle) -> usize;
138
139    /// Current physical protection posture for this exact handle.
140    fn physical_protection(
141        &self,
142        access: &ProviderAccess,
143        handle: &Self::Handle,
144    ) -> PhysicalProtection;
145
146    /// Immutable access tied to the unique live handle.
147    fn bytes<'handle>(
148        &self,
149        access: &ProviderAccess,
150        handle: &'handle Self::Handle,
151    ) -> &'handle [u8];
152
153    /// Exclusive access tied to the unique live handle.
154    fn bytes_mut<'handle>(
155        &self,
156        access: &ProviderAccess,
157        handle: &'handle mut Self::Handle,
158    ) -> &'handle mut [u8];
159
160    /// Confirms the completed overwrite/barrier at the requested assurance level.
161    fn confirm_wipe(
162        &self,
163        access: &ProviderAccess,
164        handle: &Self::Handle,
165        attestation: Option<AttestationEvidence>,
166        cursor: &mut TeardownCursor,
167    ) -> WipeConfirmation;
168
169    /// Removes physical protection conclusively or reports uncertainty.
170    fn remove_protection(
171        &self,
172        access: &ProviderAccess,
173        handle: &mut Self::Handle,
174        cursor: &mut TeardownCursor,
175    ) -> ProviderOperationResult;
176
177    /// Reconciles page/reference/accounting state exactly once.
178    fn reconcile_accounting(
179        &self,
180        access: &ProviderAccess,
181        handle: &mut Self::Handle,
182        cursor: &mut TeardownCursor,
183    ) -> ProviderOperationResult;
184
185    /// Disposes the allocation exactly once.
186    fn dispose(
187        &self,
188        access: &ProviderAccess,
189        handle: Self::Handle,
190        cursor: &mut TeardownCursor,
191    ) -> DisposalResult<Self::Handle>;
192
193    /// Transfers one still-live handle into its pre-reserved quarantine slot.
194    ///
195    /// This hook must be infallible, allocation-free, and non-unwinding.
196    fn quarantine(&self, access: &ProviderAccess, handle: Self::Handle, record: QuarantineRecord);
197}
198
199mod sealed {
200    pub trait ThreadMovable {}
201}
202
203/// Sealed proof that provider protection and teardown may move across threads.
204///
205/// This is implemented only for providers reviewed inside `base64-ng`.
206pub trait ThreadMovableProvider: ProtectedMemoryProvider + sealed::ThreadMovable {}