#[allow(dead_code)] #[cfg(all(feature = "fips", feature = "std", debug_assertions))]
pub(crate) fn get_fips_service_status() -> FipsServiceStatus<()> {
if let Some(status) = indicator::get_status() {
if status {
FipsServiceStatus::Approved(())
} else {
FipsServiceStatus::NonApproved(())
}
} else {
FipsServiceStatus::Unset(())
}
}
#[inline]
pub(crate) fn set_fips_service_status_unapproved() {
#[cfg(all(feature = "fips", feature = "std", debug_assertions))]
indicator::set_unapproved();
}
#[allow(dead_code)]
#[cfg(all(feature = "fips", feature = "std", debug_assertions))]
#[inline]
pub(crate) fn clear_fips_service_status() {
indicator::clear();
}
#[cfg(all(feature = "fips", feature = "std", debug_assertions))]
pub(crate) mod indicator {
use core::cell::Cell;
std::thread_local! {
static STATUS_INDICATOR: Cell<Option<bool>> = const { Cell::new(None) };
}
pub fn get_status() -> Option<bool> {
STATUS_INDICATOR.with(|v| {
let swap = Cell::new(None);
v.swap(&swap);
swap.take()
})
}
pub fn set_approved() {
STATUS_INDICATOR.with(|v| v.set(Some(true)));
}
pub fn set_unapproved() {
STATUS_INDICATOR.with(|v| v.set(Some(false)));
}
pub fn clear() {
STATUS_INDICATOR.with(|v| v.set(None));
}
}
#[cfg(all(feature = "fips", feature = "std", debug_assertions))]
#[inline]
pub(crate) fn service_indicator_before_call() -> u64 {
0
}
#[cfg(all(feature = "fips", feature = "std", debug_assertions))]
#[inline]
pub(crate) fn service_indicator_after_call() -> u64 {
0
}
#[allow(dead_code)] #[cfg(all(feature = "fips", feature = "std", debug_assertions))]
#[allow(clippy::module_name_repetitions)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum FipsServiceStatus<R> {
Approved(R),
NonApproved(R),
Unset(R),
}
#[cfg(all(feature = "fips", feature = "std", debug_assertions))]
impl<R> FipsServiceStatus<R> {
#[allow(dead_code)]
pub fn map<S, F>(self, op: F) -> FipsServiceStatus<S>
where
F: FnOnce(R) -> S,
{
match self {
FipsServiceStatus::Approved(v) => FipsServiceStatus::Approved(op(v)),
FipsServiceStatus::NonApproved(v) => FipsServiceStatus::NonApproved(op(v)),
FipsServiceStatus::Unset(v) => FipsServiceStatus::Unset(op(v)),
}
}
}
macro_rules! indicator_check {
($function:expr) => {{
#[cfg(all(feature = "fips", feature = "std", debug_assertions))]
{
use crate::fips::{service_indicator_after_call, service_indicator_before_call};
let before = service_indicator_before_call();
let result = $function;
let after = service_indicator_after_call();
if before == after {
crate::fips::indicator::set_unapproved();
result
} else {
crate::fips::indicator::set_approved();
result
}
}
#[cfg(any(not(feature = "fips"), not(debug_assertions)))]
{
$function
}
}};
}
pub(crate) use indicator_check;
#[allow(unused_macros)]
#[cfg(all(feature = "fips", feature = "std", debug_assertions))]
macro_rules! check_fips_service_status {
($function:expr) => {{
use $crate::fips::{clear_fips_service_status, get_fips_service_status};
clear_fips_service_status();
let result = $function;
get_fips_service_status().map(|()| result)
}};
}
#[allow(unused_imports)]
#[cfg(all(feature = "fips", feature = "std", debug_assertions))]
pub(crate) use check_fips_service_status;
#[allow(unused_macros)]
#[cfg(all(feature = "fips", feature = "std", debug_assertions))]
macro_rules! assert_fips_status_indicator {
($function:expr, $expect:path) => {
assert_fips_status_indicator!($function, $expect, "unexpected service indicator")
};
($function:expr, $expect:path, $message:literal) => {{
match crate::fips::check_fips_service_status!($function) {
$expect(v) => v,
_ => panic!($message),
}
}};
}
#[allow(unused_imports)]
#[cfg(all(feature = "fips", feature = "std", debug_assertions))]
pub(crate) use assert_fips_status_indicator;
#[cfg(test)]
mod tests {
#[cfg(all(feature = "fips", feature = "std", debug_assertions))]
#[test]
fn test_service_status() {
use crate::fips::FipsServiceStatus;
assert_eq!(
FipsServiceStatus::Approved(true),
FipsServiceStatus::Approved(()).map(|()| true)
);
assert_eq!(
FipsServiceStatus::NonApproved(true),
FipsServiceStatus::NonApproved(()).map(|()| true)
);
assert_eq!(
FipsServiceStatus::Unset(true),
FipsServiceStatus::Unset(()).map(|()| true)
);
}
}