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