1use hmac::{Hmac, Mac};
2use sha2::{Digest, Sha256, Sha512};
14use sha3::Sha3_512;
15use std::sync::atomic::{AtomicBool, Ordering};
16use zeroize::Zeroize;
17
18static FIPS_MODE_ENABLED: AtomicBool = AtomicBool::new(true);
20
21#[derive(Debug, Clone, Copy, PartialEq)]
30pub enum FipsSecurityLevel {
31 Level1 = 1,
33 Level2 = 2,
35 Level3 = 3,
37 Level4 = 4,
39}
40
41#[derive(Debug, Clone)]
44pub enum FipsApprovedAlgorithm {
45 Sha256,
47 Sha512,
49 Sha3_512,
51 HmacSha256,
53 HmacSha512,
55 Aes256Gcm,
57}
58
59#[allow(dead_code)]
68pub struct FipsModule {
69 security_level: FipsSecurityLevel,
71 self_test_passed: bool,
73 version: String,
75}
76
77impl Default for FipsModule {
78 fn default() -> Self {
79 Self::new()
80 }
81}
82
83impl FipsModule {
84 pub fn new() -> Self {
86 FipsModule {
87 security_level: FipsSecurityLevel::Level1,
88 self_test_passed: false,
89 version: "1.0.0".to_string(),
90 }
91 }
92
93 pub fn power_on_self_test(&mut self) -> Result<(), String> {
97 let sha256_result = self.test_sha256()?;
101
102 let sha512_result = self.test_sha512()?;
104
105 let sha3_512_result = self.test_sha3_512()?;
107
108 let hmac_sha256_result = self.test_hmac_sha256()?;
110
111 let rng_result = self.test_rng()?;
113
114 if sha256_result && sha512_result && sha3_512_result && hmac_sha256_result && rng_result {
116 self.self_test_passed = true;
117 Ok(())
118 } else {
119 self.self_test_passed = false;
120 Err("FIPS 140-3 self-tests failed".to_string())
121 }
122 }
123
124 fn test_sha256(&self) -> Result<bool, String> {
127 let test_input = b"abc";
128 let expected_output = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad";
129
130 let mut hasher = Sha256::new();
131 hasher.update(test_input);
132 let result = hasher.finalize();
133 let result_hex = hex::encode(result);
134
135 if result_hex == expected_output {
136 Ok(true)
137 } else {
138 Err("SHA-256 KAT failed".to_string())
139 }
140 }
141
142 fn test_sha512(&self) -> Result<bool, String> {
145 let test_input = b"abc";
146 let expected_output = "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f";
147
148 let mut hasher = Sha512::new();
149 hasher.update(test_input);
150 let result = hasher.finalize();
151 let result_hex = hex::encode(result);
152
153 if result_hex == expected_output {
154 Ok(true)
155 } else {
156 Err("SHA-512 KAT failed".to_string())
157 }
158 }
159
160 fn test_sha3_512(&self) -> Result<bool, String> {
163 let test_input = b"abc";
164 let expected_output = "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0";
165
166 let mut hasher = Sha3_512::new();
167 hasher.update(test_input);
168 let result = hasher.finalize();
169 let result_hex = hex::encode(result);
170
171 if result_hex == expected_output {
172 Ok(true)
173 } else {
174 Err("SHA3-512 KAT failed".to_string())
175 }
176 }
177
178 fn test_hmac_sha256(&self) -> Result<bool, String> {
181 let key = b"key";
182 let message = b"The quick brown fox jumps over the lazy dog";
183 let expected_output = "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8";
184
185 type HmacSha256 = Hmac<Sha256>;
186 let mut mac = HmacSha256::new_from_slice(key).map_err(|_| "HMAC initialization failed")?;
187 mac.update(message);
188 let result = mac.finalize();
189 let result_hex = hex::encode(result.into_bytes());
190
191 if result_hex == expected_output {
192 Ok(true)
193 } else {
194 Err("HMAC-SHA-256 KAT failed".to_string())
195 }
196 }
197
198 fn test_rng(&self) -> Result<bool, String> {
202 use aes_gcm::aead::rand_core::RngCore;
203 use aes_gcm::aead::OsRng;
204
205 let mut block_a = [0u8; 32];
207 let mut block_b = [0u8; 32];
208 OsRng.fill_bytes(&mut block_a);
209 OsRng.fill_bytes(&mut block_b);
210
211 if block_a == block_b {
213 return Err("FIPS RNG test failed: consecutive outputs are identical".to_string());
214 }
215
216 if block_a.iter().all(|&b| b == 0) || block_a.iter().all(|&b| b == 0xFF) {
218 return Err("FIPS RNG test failed: output stuck at constant value".to_string());
219 }
220 if block_b.iter().all(|&b| b == 0) || block_b.iter().all(|&b| b == 0xFF) {
221 return Err("FIPS RNG test failed: output stuck at constant value".to_string());
222 }
223
224 block_a.zeroize();
226 block_b.zeroize();
227
228 Ok(true)
229 }
230
231 pub fn conditional_self_test(&self, algorithm: FipsApprovedAlgorithm) -> Result<(), String> {
234 if !self.self_test_passed {
235 return Err("Power-on self-tests not completed".to_string());
236 }
237
238 match algorithm {
240 FipsApprovedAlgorithm::Sha256 => self.test_sha256().map(|_| ()),
241 FipsApprovedAlgorithm::Sha512 => self.test_sha512().map(|_| ()),
242 FipsApprovedAlgorithm::Sha3_512 => self.test_sha3_512().map(|_| ()),
243 FipsApprovedAlgorithm::HmacSha256 => self.test_hmac_sha256().map(|_| ()),
244 _ => Ok(()),
245 }
246 }
247
248 pub fn is_fips_mode(&self) -> bool {
250 FIPS_MODE_ENABLED.load(Ordering::SeqCst)
251 }
252
253 pub fn enable_fips_mode() {
255 FIPS_MODE_ENABLED.store(true, Ordering::SeqCst);
256 }
257
258 pub fn disable_fips_mode() {
260 FIPS_MODE_ENABLED.store(false, Ordering::SeqCst);
261 }
262
263 pub fn security_level(&self) -> FipsSecurityLevel {
265 self.security_level
266 }
267
268 pub fn self_test_status(&self) -> bool {
270 self.self_test_passed
271 }
272}
273
274#[derive(Zeroize)]
277#[zeroize(drop)]
278pub struct SecureKey {
279 key_material: Vec<u8>,
280}
281
282impl SecureKey {
283 pub fn new(key_material: Vec<u8>) -> Self {
285 SecureKey { key_material }
286 }
287
288 pub fn as_bytes(&self) -> &[u8] {
290 &self.key_material
291 }
292}
293
294pub struct FipsHash;
296
297impl FipsHash {
298 pub fn sha512(data: &[u8]) -> Vec<u8> {
300 let mut hasher = Sha512::new();
301 hasher.update(data);
302 hasher.finalize().to_vec()
303 }
304
305 pub fn sha3_512(data: &[u8]) -> Vec<u8> {
307 let mut hasher = Sha3_512::new();
308 hasher.update(data);
309 hasher.finalize().to_vec()
310 }
311
312 pub fn sha256(data: &[u8]) -> Vec<u8> {
314 let mut hasher = Sha256::new();
315 hasher.update(data);
316 hasher.finalize().to_vec()
317 }
318}
319
320pub struct FipsHmac;
322
323impl FipsHmac {
324 pub fn hmac_sha512(key: &[u8], data: &[u8]) -> Result<Vec<u8>, String> {
326 type HmacSha512 = Hmac<Sha512>;
327 let mut mac =
328 HmacSha512::new_from_slice(key).map_err(|_| "HMAC key initialization failed")?;
329 mac.update(data);
330 Ok(mac.finalize().into_bytes().to_vec())
331 }
332
333 pub fn verify_hmac_sha512(key: &[u8], data: &[u8], tag: &[u8]) -> Result<(), String> {
335 type HmacSha512 = Hmac<Sha512>;
336 let mut mac =
337 HmacSha512::new_from_slice(key).map_err(|_| "HMAC key initialization failed")?;
338 mac.update(data);
339 mac.verify_slice(tag)
340 .map_err(|_| "HMAC verification failed".to_string())
341 }
342}
343
344static SELF_TESTS: std::sync::OnceLock<Result<(), String>> = std::sync::OnceLock::new();
346
347pub fn ensure_self_tests() -> Result<(), String> {
359 SELF_TESTS
360 .get_or_init(|| FipsModule::new().power_on_self_test())
361 .clone()
362}
363
364#[cfg(test)]
365mod self_test_gate {
366 use super::*;
367
368 #[test]
369 fn test_ensure_self_tests_passes_and_is_idempotent() {
370 assert!(ensure_self_tests().is_ok());
371 assert!(ensure_self_tests().is_ok());
374 }
375
376 #[test]
383 fn test_the_gate_does_not_block_encryption() {
384 assert!(
385 ensure_self_tests().is_ok(),
386 "the self-tests gate every engine construction; a failure here \
387 takes all encryption with it"
388 );
389 }
390}
391
392#[cfg(test)]
393mod tests {
394 use super::*;
395
396 #[test]
397 fn test_fips_power_on_self_test() {
398 let mut module = FipsModule::new();
399 assert!(module.power_on_self_test().is_ok());
400 assert!(module.self_test_status());
401 }
402
403 #[test]
404 fn test_sha256_kat() {
405 let module = FipsModule::new();
406 assert!(module.test_sha256().is_ok());
407 }
408
409 #[test]
410 fn test_sha512_kat() {
411 let module = FipsModule::new();
412 assert!(module.test_sha512().is_ok());
413 }
414
415 #[test]
416 fn test_sha3_512_kat() {
417 let module = FipsModule::new();
418 assert!(module.test_sha3_512().is_ok());
419 }
420
421 #[test]
422 fn test_hmac_sha256_kat() {
423 let module = FipsModule::new();
424 assert!(module.test_hmac_sha256().is_ok());
425 }
426
427 #[test]
428 fn test_secure_key_zeroization() {
429 let key = SecureKey::new(vec![1, 2, 3, 4, 5]);
430 assert_eq!(key.as_bytes(), &[1, 2, 3, 4, 5]);
431 drop(key);
432 }
434
435 #[test]
436 fn test_fips_hash_sha512() {
437 let data = b"test data";
438 let hash = FipsHash::sha512(data);
439 assert_eq!(hash.len(), 64); }
441
442 #[test]
443 fn test_fips_hmac() {
444 let key = b"secret key";
445 let data = b"message";
446 let tag = FipsHmac::hmac_sha512(key, data).unwrap();
447 assert!(FipsHmac::verify_hmac_sha512(key, data, &tag).is_ok());
448 }
449}