1use super::{
2 Backend, BackendHealthPosture, BackendPolicy, CandidateDetectionMode, CtGatePosture,
3 MemoryLockPosture, OperationBackendReport, OperationKind, OperationSecurityPosture,
4 SecurityPosture, WasmArtifactPosture, WasmRuntimePosture, WipePosture,
5 operation::{wasm_artifact_posture, wasm_runtime_posture},
6};
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9pub struct BackendPolicyError {
10 pub policy: BackendPolicy,
12 pub report: BackendReport,
14}
15impl core::fmt::Display for BackendPolicyError {
16 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
17 write!(
18 formatter,
19 "runtime backend policy `{}` was not satisfied ({})",
20 self.policy, self.report,
21 )
22 }
23}
24
25#[cfg(feature = "std")]
26impl std::error::Error for BackendPolicyError {}
27#[derive(Clone, Copy, Debug, Eq, PartialEq)]
29#[allow(clippy::struct_excessive_bools)]
30pub struct BackendReport {
31 pub active: Backend,
34 pub accelerated_backend_active: bool,
37 pub security_posture: SecurityPosture,
40 pub encode_backend: OperationBackendReport,
45 pub strict_decode_backend: OperationBackendReport,
47 pub secret_decode_backend: OperationBackendReport,
50 pub candidate: Backend,
52 pub candidate_detection_mode: CandidateDetectionMode,
55 pub simd_feature_enabled: bool,
57 pub ordinary_acceleration_active: bool,
59 pub unsafe_boundary_enforced: bool,
66 pub wasm_artifact_posture: WasmArtifactPosture,
68 pub wasm_runtime_posture: WasmRuntimePosture,
70 pub wipe_posture: WipePosture,
72 pub ct_gate_posture: CtGatePosture,
74}
75#[derive(Clone, Copy, Debug, Eq, PartialEq)]
77#[allow(clippy::struct_excessive_bools)]
78pub struct BackendSnapshot {
79 pub active: &'static str,
81 pub accelerated_backend_active: bool,
83 pub security_posture: &'static str,
85 pub encode_backend: super::OperationBackendSnapshot,
87 pub strict_decode_backend: super::OperationBackendSnapshot,
89 pub secret_decode_backend: super::OperationBackendSnapshot,
91 pub candidate: &'static str,
93 pub candidate_detection_mode: &'static str,
95 pub candidate_required_cpu_features: &'static [&'static str],
97 pub simd_feature_enabled: bool,
99 pub ordinary_acceleration_active: bool,
101 pub unsafe_boundary_enforced: bool,
107 pub wasm_artifact_posture: &'static str,
109 pub wasm_runtime_posture: &'static str,
111 pub wipe_posture: &'static str,
113 pub ct_gate_posture: &'static str,
115}
116
117impl core::fmt::Display for BackendReport {
118 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
119 write!(
120 formatter,
121 "active={} accelerated_backend_active={} security_posture={} encode_backend={} strict_decode_backend={} secret_decode_backend={} candidate={} candidate_detection_mode={} candidate_required_cpu_features=",
122 self.active,
123 self.accelerated_backend_active,
124 self.security_posture,
125 self.encode_backend.backend,
126 self.strict_decode_backend.backend,
127 self.secret_decode_backend.backend,
128 self.candidate,
129 self.candidate_detection_mode,
130 )?;
131 write_feature_list(formatter, self.candidate_required_cpu_features())?;
132 write!(
133 formatter,
134 " simd_feature_enabled={} ordinary_acceleration_active={} unsafe_boundary_enforced={} wasm_artifact_posture={} wasm_runtime_posture={} wipe_posture={} ct_gate_posture={}",
135 self.simd_feature_enabled,
136 self.ordinary_acceleration_active,
137 self.unsafe_boundary_enforced,
138 self.wasm_artifact_posture.as_str(),
139 self.wasm_runtime_posture.as_str(),
140 self.wipe_posture,
141 self.ct_gate_posture,
142 )
143 }
144}
145
146impl BackendReport {
147 #[must_use]
157 pub const fn satisfies(self, policy: BackendPolicy) -> bool {
158 match policy {
159 BackendPolicy::ScalarExecutionOnly => {
160 stably_scalar(self.encode_backend)
161 && stably_scalar(self.strict_decode_backend)
162 && !self.ordinary_acceleration_active
163 }
164 BackendPolicy::SimdFeatureDisabled => !self.simd_feature_enabled,
165 BackendPolicy::NoDetectedSimdCandidate => matches!(self.candidate, Backend::Scalar),
166 BackendPolicy::HighAssuranceScalarOnly => {
167 matches!(
168 self.encode_backend.security_posture,
169 OperationSecurityPosture::OrdinaryScalar
170 ) && matches!(
171 self.strict_decode_backend.security_posture,
172 OperationSecurityPosture::OrdinaryScalar
173 ) && matches!(
174 self.secret_decode_backend.security_posture,
175 OperationSecurityPosture::ScalarConstantTimeOriented
176 ) && matches!(self.candidate, Backend::Scalar)
177 && !self.simd_feature_enabled
178 && !self.ordinary_acceleration_active
179 && self.unsafe_boundary_enforced
180 && matches!(
181 self.ct_gate_posture,
182 CtGatePosture::HardwareSpeculationBarrier
183 | CtGatePosture::HardwareSpeculationBarrierBuildAsserted
184 )
185 }
186 }
187 }
188
189 #[must_use]
200 pub const fn candidate_required_cpu_features(self) -> &'static [&'static str] {
201 self.candidate.required_cpu_features()
202 }
203
204 #[must_use]
208 pub fn active_decode_backend(self) -> Backend {
209 let _ = self;
210 active_decode_backend()
211 }
212
213 #[must_use]
221 pub const fn memory_lock_posture(self) -> MemoryLockPosture {
222 let _ = self;
223 MemoryLockPosture::NotProvided
224 }
225
226 #[must_use]
238 pub const fn snapshot(self) -> BackendSnapshot {
239 BackendSnapshot {
240 active: self.active.as_str(),
241 accelerated_backend_active: self.accelerated_backend_active,
242 security_posture: self.security_posture.as_str(),
243 encode_backend: self.encode_backend.snapshot(),
244 strict_decode_backend: self.strict_decode_backend.snapshot(),
245 secret_decode_backend: self.secret_decode_backend.snapshot(),
246 candidate: self.candidate.as_str(),
247 candidate_detection_mode: self.candidate_detection_mode.as_str(),
248 candidate_required_cpu_features: self.candidate_required_cpu_features(),
249 simd_feature_enabled: self.simd_feature_enabled,
250 ordinary_acceleration_active: self.ordinary_acceleration_active,
251 unsafe_boundary_enforced: self.unsafe_boundary_enforced,
252 wasm_artifact_posture: self.wasm_artifact_posture.as_str(),
253 wasm_runtime_posture: self.wasm_runtime_posture.as_str(),
254 wipe_posture: self.wipe_posture.as_str(),
255 ct_gate_posture: self.ct_gate_posture.as_str(),
256 }
257 }
258}
259
260const fn stably_scalar(report: OperationBackendReport) -> bool {
261 matches!(
262 report.security_posture,
263 OperationSecurityPosture::OrdinaryScalar
264 ) && matches!(
265 report.health_posture,
266 BackendHealthPosture::ScalarFixed
267 | BackendHealthPosture::Quarantined
268 | BackendHealthPosture::SynchronizationUnavailable
269 )
270}
271
272#[must_use]
283pub fn backend_report() -> BackendReport {
284 let encode = active_backend();
285 let strict_decode = active_decode_backend();
286 let encode_candidate = encode_candidate_backend();
287 let decode_candidate = decode_candidate_backend();
288 let candidate = detected_candidate();
289 let candidate_detection_mode = candidate_detection_mode();
290 let accelerated_backend_active = encode != Backend::Scalar;
291 let ordinary_acceleration_active =
292 encode != Backend::Scalar || strict_decode != Backend::Scalar;
293 let unsafe_boundary_enforced = !cfg!(feature = "simd");
294 let security_posture = if accelerated_backend_active {
295 SecurityPosture::Accelerated
296 } else if candidate == Backend::Scalar {
297 SecurityPosture::ScalarOnly
298 } else {
299 SecurityPosture::SimdCandidateScalarActive
300 };
301
302 BackendReport {
303 active: encode,
304 accelerated_backend_active,
305 security_posture,
306 encode_backend: OperationBackendReport::ordinary(
307 OperationKind::Encode,
308 encode,
309 encode_candidate,
310 ),
311 strict_decode_backend: OperationBackendReport::ordinary(
312 OperationKind::StrictDecode,
313 strict_decode,
314 decode_candidate,
315 ),
316 secret_decode_backend: OperationBackendReport::secret_decode(),
317 candidate,
318 candidate_detection_mode,
319 simd_feature_enabled: cfg!(feature = "simd"),
320 ordinary_acceleration_active,
321 unsafe_boundary_enforced,
322 wasm_artifact_posture: wasm_artifact_posture(),
323 wasm_runtime_posture: wasm_runtime_posture(),
324 wipe_posture: wipe_posture(),
325 ct_gate_posture: ct_gate_posture(),
326 }
327}
328
329const fn wipe_posture() -> WipePosture {
330 if cfg!(any(
331 target_arch = "aarch64",
332 target_arch = "arm",
333 target_arch = "riscv32",
334 target_arch = "riscv64",
335 target_arch = "x86",
336 target_arch = "x86_64",
337 )) {
338 WipePosture::HardwareFence
339 } else {
340 WipePosture::CompilerFenceOnly
341 }
342}
343
344const fn ct_gate_posture() -> CtGatePosture {
345 if cfg!(any(target_arch = "x86", target_arch = "x86_64")) {
346 CtGatePosture::HardwareSpeculationBarrier
347 } else if cfg!(all(
348 target_arch = "aarch64",
349 base64_ng_aarch64_csdb_attested
350 )) {
351 CtGatePosture::HardwareSpeculationBarrierBuildAsserted
352 } else if cfg!(target_arch = "aarch64") {
353 CtGatePosture::HardwareSpeculationBarrierUnattested
354 } else if cfg!(any(
355 target_arch = "arm",
356 target_arch = "riscv32",
357 target_arch = "riscv64"
358 )) {
359 CtGatePosture::OrderingFence
360 } else {
361 CtGatePosture::CompilerFenceOnly
362 }
363}
364
365pub fn require_backend_policy(policy: BackendPolicy) -> Result<(), BackendPolicyError> {
377 let report = backend_report();
378 if report.satisfies(policy) {
379 Ok(())
380 } else {
381 Err(BackendPolicyError { policy, report })
382 }
383}
384
385fn write_feature_list(
386 formatter: &mut core::fmt::Formatter<'_>,
387 features: &[&str],
388) -> core::fmt::Result {
389 formatter.write_str("[")?;
390 let mut index = 0;
391 while index < features.len() {
392 if index != 0 {
393 formatter.write_str(",")?;
394 }
395 formatter.write_str(features[index])?;
396 index += 1;
397 }
398 formatter.write_str("]")
399}
400
401#[cfg(feature = "simd")]
402fn active_backend() -> Backend {
403 crate::encode_backend::active_encode_backend().reported()
404}
405
406#[cfg(not(feature = "simd"))]
407const fn active_backend() -> Backend {
408 Backend::Scalar
409}
410
411#[cfg(feature = "simd")]
412fn active_decode_backend() -> Backend {
413 crate::decode_backend::active_decode_backend().reported()
414}
415
416#[cfg(not(feature = "simd"))]
417const fn active_decode_backend() -> Backend {
418 Backend::Scalar
419}
420
421#[cfg(feature = "simd")]
422fn encode_candidate_backend() -> Backend {
423 crate::encode_backend::candidate_encode_backend().reported()
424}
425
426#[cfg(not(feature = "simd"))]
427const fn encode_candidate_backend() -> Backend {
428 Backend::Scalar
429}
430
431#[cfg(feature = "simd")]
432fn decode_candidate_backend() -> Backend {
433 crate::decode_backend::candidate_decode_backend().reported()
434}
435
436#[cfg(not(feature = "simd"))]
437const fn decode_candidate_backend() -> Backend {
438 Backend::Scalar
439}
440
441#[cfg(feature = "simd")]
442fn detected_candidate() -> Backend {
443 match crate::simd::detected_candidate() {
444 crate::simd::Candidate::Scalar => Backend::Scalar,
445 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
446 crate::simd::Candidate::Avx512Vbmi => Backend::Avx512Vbmi,
447 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
448 crate::simd::Candidate::Avx2 => Backend::Avx2,
449 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
450 crate::simd::Candidate::Ssse3Sse41 => Backend::Ssse3Sse41,
451 #[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
452 crate::simd::Candidate::Neon => Backend::Neon,
453 #[cfg(target_arch = "wasm32")]
454 crate::simd::Candidate::WasmSimd128 => Backend::WasmSimd128,
455 #[cfg(target_arch = "riscv64")]
456 crate::simd::Candidate::Rvv => Backend::Rvv,
457 #[cfg(all(target_arch = "aarch64", base64_ng_sve_candidate))]
458 crate::simd::Candidate::Sve => Backend::Sve,
459 }
460}
461
462#[cfg(not(feature = "simd"))]
463const fn detected_candidate() -> Backend {
464 Backend::Scalar
465}
466
467#[cfg(all(
468 feature = "simd",
469 feature = "std",
470 any(
471 target_arch = "x86",
472 target_arch = "x86_64",
473 all(target_arch = "aarch64", base64_ng_sve_candidate),
474 target_arch = "riscv64"
475 )
476))]
477const fn candidate_detection_mode() -> CandidateDetectionMode {
478 CandidateDetectionMode::RuntimeCpuFeatures
479}
480
481#[cfg(all(
482 feature = "simd",
483 not(all(
484 feature = "std",
485 any(
486 target_arch = "x86",
487 target_arch = "x86_64",
488 all(target_arch = "aarch64", base64_ng_sve_candidate),
489 target_arch = "riscv64"
490 )
491 ))
492))]
493const fn candidate_detection_mode() -> CandidateDetectionMode {
494 CandidateDetectionMode::CompileTimeTargetFeatures
495}
496
497#[cfg(not(feature = "simd"))]
498const fn candidate_detection_mode() -> CandidateDetectionMode {
499 CandidateDetectionMode::SimdFeatureDisabled
500}