1#[allow(dead_code)] #[cfg(all(feature = "fips", debug_assertions))]
7pub(crate) fn get_fips_service_status() -> FipsServiceStatus<()> {
8 if let Some(status) = indicator::get_status() {
9 if status {
10 FipsServiceStatus::Approved(())
11 } else {
12 FipsServiceStatus::NonApproved(())
13 }
14 } else {
15 FipsServiceStatus::Unset(())
16 }
17}
18
19#[inline]
20pub(crate) fn set_fips_service_status_unapproved() {
21 #[cfg(all(feature = "fips", debug_assertions))]
22 indicator::set_unapproved();
23}
24
25#[allow(dead_code)]
26#[cfg(all(feature = "fips", debug_assertions))]
27#[inline]
28pub(crate) fn clear_fips_service_status() {
29 indicator::clear();
30}
31
32#[cfg(all(feature = "fips", debug_assertions))]
33pub(crate) mod indicator {
34 use core::cell::Cell;
35
36 thread_local! {
37 static STATUS_INDICATOR: Cell<Option<bool>> = const { Cell::new(None) };
38 }
39
40 pub fn get_status() -> Option<bool> {
43 STATUS_INDICATOR.with(|v| {
44 let swap = Cell::new(None);
45 v.swap(&swap);
46 swap.take()
47 })
48 }
49
50 pub fn set_approved() {
51 STATUS_INDICATOR.with(|v| v.set(Some(true)));
52 }
53
54 pub fn set_unapproved() {
55 STATUS_INDICATOR.with(|v| v.set(Some(false)));
56 }
57
58 pub fn clear() {
59 STATUS_INDICATOR.with(|v| v.set(None));
60 }
61}
62
63#[cfg(all(feature = "fips", debug_assertions))]
64#[inline]
65pub(crate) fn service_indicator_before_call() -> u64 {
66 unsafe { aws_lc::FIPS_service_indicator_before_call() }
67}
68
69#[cfg(all(feature = "fips", debug_assertions))]
70#[inline]
71pub(crate) fn service_indicator_after_call() -> u64 {
72 unsafe { aws_lc::FIPS_service_indicator_after_call() }
73}
74
75#[allow(dead_code)] #[cfg(all(feature = "fips", debug_assertions))]
78#[allow(clippy::module_name_repetitions)]
79#[derive(Clone, Copy, Debug, PartialEq, Eq)]
80pub(crate) enum FipsServiceStatus<R> {
81 Approved(R),
83
84 NonApproved(R),
89
90 Unset(R),
92}
93
94#[cfg(all(feature = "fips", debug_assertions))]
95impl<R> FipsServiceStatus<R> {
96 #[allow(dead_code)]
98 pub fn map<S, F>(self, op: F) -> FipsServiceStatus<S>
99 where
100 F: FnOnce(R) -> S,
101 {
102 match self {
103 FipsServiceStatus::Approved(v) => FipsServiceStatus::Approved(op(v)),
104 FipsServiceStatus::NonApproved(v) => FipsServiceStatus::NonApproved(op(v)),
105 FipsServiceStatus::Unset(v) => FipsServiceStatus::Unset(op(v)),
106 }
107 }
108}
109
110macro_rules! indicator_check {
111 ($function:expr) => {{
112 #[cfg(all(feature = "fips", debug_assertions))]
113 {
114 use crate::fips::{service_indicator_after_call, service_indicator_before_call};
115 let before = service_indicator_before_call();
116 let result = $function;
117 let after = service_indicator_after_call();
118 if before == after {
119 crate::fips::indicator::set_unapproved();
120 result
121 } else {
122 crate::fips::indicator::set_approved();
123 result
124 }
125 }
126 #[cfg(any(not(feature = "fips"), not(debug_assertions)))]
127 {
128 $function
129 }
130 }};
131}
132
133pub(crate) use indicator_check;
134
135#[allow(unused_macros)]
136#[cfg(all(feature = "fips", debug_assertions))]
137macro_rules! check_fips_service_status {
138 ($function:expr) => {{
139 use $crate::fips::{clear_fips_service_status, get_fips_service_status};
141 clear_fips_service_status();
142 let result = $function;
144 get_fips_service_status().map(|()| result)
146 }};
147}
148
149#[allow(unused_imports)]
150#[cfg(all(feature = "fips", debug_assertions))]
151pub(crate) use check_fips_service_status;
152
153#[allow(unused_macros)]
154#[cfg(all(feature = "fips", debug_assertions))]
155macro_rules! assert_fips_status_indicator {
156 ($function:expr, $expect:path) => {
157 assert_fips_status_indicator!($function, $expect, "unexpected service indicator")
158 };
159 ($function:expr, $expect:path, $message:literal) => {{
160 match crate::fips::check_fips_service_status!($function) {
161 $expect(v) => v,
162 _ => panic!($message),
163 }
164 }};
165}
166
167#[allow(unused_imports)]
168#[cfg(all(feature = "fips", debug_assertions))]
169pub(crate) use assert_fips_status_indicator;
170
171#[cfg(test)]
172mod tests {
173
174 #[cfg(all(feature = "fips", debug_assertions))]
175 #[test]
176 fn test_service_status() {
177 use crate::fips::FipsServiceStatus;
178
179 assert_eq!(
180 FipsServiceStatus::Approved(true),
181 FipsServiceStatus::Approved(()).map(|()| true)
182 );
183 assert_eq!(
184 FipsServiceStatus::NonApproved(true),
185 FipsServiceStatus::NonApproved(()).map(|()| true)
186 );
187 assert_eq!(
188 FipsServiceStatus::Unset(true),
189 FipsServiceStatus::Unset(()).map(|()| true)
190 );
191 }
192}