use hmac::{Hmac, Mac};
use sha2::{Digest, Sha256, Sha512};
use sha3::Sha3_512;
use std::sync::atomic::{AtomicBool, Ordering};
use zeroize::Zeroize;
static FIPS_MODE_ENABLED: AtomicBool = AtomicBool::new(true);
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FipsSecurityLevel {
Level1 = 1,
Level2 = 2,
Level3 = 3,
Level4 = 4,
}
#[derive(Debug, Clone)]
pub enum FipsApprovedAlgorithm {
Sha256,
Sha512,
Sha3_512,
HmacSha256,
HmacSha512,
Aes256Gcm,
}
#[allow(dead_code)]
pub struct FipsModule {
security_level: FipsSecurityLevel,
self_test_passed: bool,
version: String,
}
impl Default for FipsModule {
fn default() -> Self {
Self::new()
}
}
impl FipsModule {
pub fn new() -> Self {
FipsModule {
security_level: FipsSecurityLevel::Level1,
self_test_passed: false,
version: "1.0.0".to_string(),
}
}
pub fn power_on_self_test(&mut self) -> Result<(), String> {
let sha256_result = self.test_sha256()?;
let sha512_result = self.test_sha512()?;
let sha3_512_result = self.test_sha3_512()?;
let hmac_sha256_result = self.test_hmac_sha256()?;
let rng_result = self.test_rng()?;
if sha256_result && sha512_result && sha3_512_result && hmac_sha256_result && rng_result {
self.self_test_passed = true;
Ok(())
} else {
self.self_test_passed = false;
Err("FIPS 140-3 self-tests failed".to_string())
}
}
fn test_sha256(&self) -> Result<bool, String> {
let test_input = b"abc";
let expected_output = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad";
let mut hasher = Sha256::new();
hasher.update(test_input);
let result = hasher.finalize();
let result_hex = hex::encode(result);
if result_hex == expected_output {
Ok(true)
} else {
Err("SHA-256 KAT failed".to_string())
}
}
fn test_sha512(&self) -> Result<bool, String> {
let test_input = b"abc";
let expected_output = "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f";
let mut hasher = Sha512::new();
hasher.update(test_input);
let result = hasher.finalize();
let result_hex = hex::encode(result);
if result_hex == expected_output {
Ok(true)
} else {
Err("SHA-512 KAT failed".to_string())
}
}
fn test_sha3_512(&self) -> Result<bool, String> {
let test_input = b"abc";
let expected_output = "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0";
let mut hasher = Sha3_512::new();
hasher.update(test_input);
let result = hasher.finalize();
let result_hex = hex::encode(result);
if result_hex == expected_output {
Ok(true)
} else {
Err("SHA3-512 KAT failed".to_string())
}
}
fn test_hmac_sha256(&self) -> Result<bool, String> {
let key = b"key";
let message = b"The quick brown fox jumps over the lazy dog";
let expected_output = "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8";
type HmacSha256 = Hmac<Sha256>;
let mut mac = HmacSha256::new_from_slice(key).map_err(|_| "HMAC initialization failed")?;
mac.update(message);
let result = mac.finalize();
let result_hex = hex::encode(result.into_bytes());
if result_hex == expected_output {
Ok(true)
} else {
Err("HMAC-SHA-256 KAT failed".to_string())
}
}
fn test_rng(&self) -> Result<bool, String> {
use aes_gcm::aead::rand_core::RngCore;
use aes_gcm::aead::OsRng;
let mut block_a = [0u8; 32];
let mut block_b = [0u8; 32];
OsRng.fill_bytes(&mut block_a);
OsRng.fill_bytes(&mut block_b);
if block_a == block_b {
return Err("FIPS RNG test failed: consecutive outputs are identical".to_string());
}
if block_a.iter().all(|&b| b == 0) || block_a.iter().all(|&b| b == 0xFF) {
return Err("FIPS RNG test failed: output stuck at constant value".to_string());
}
if block_b.iter().all(|&b| b == 0) || block_b.iter().all(|&b| b == 0xFF) {
return Err("FIPS RNG test failed: output stuck at constant value".to_string());
}
block_a.zeroize();
block_b.zeroize();
Ok(true)
}
pub fn conditional_self_test(&self, algorithm: FipsApprovedAlgorithm) -> Result<(), String> {
if !self.self_test_passed {
return Err("Power-on self-tests not completed".to_string());
}
match algorithm {
FipsApprovedAlgorithm::Sha256 => self.test_sha256().map(|_| ()),
FipsApprovedAlgorithm::Sha512 => self.test_sha512().map(|_| ()),
FipsApprovedAlgorithm::Sha3_512 => self.test_sha3_512().map(|_| ()),
FipsApprovedAlgorithm::HmacSha256 => self.test_hmac_sha256().map(|_| ()),
_ => Ok(()),
}
}
pub fn is_fips_mode(&self) -> bool {
FIPS_MODE_ENABLED.load(Ordering::SeqCst)
}
pub fn enable_fips_mode() {
FIPS_MODE_ENABLED.store(true, Ordering::SeqCst);
}
pub fn disable_fips_mode() {
FIPS_MODE_ENABLED.store(false, Ordering::SeqCst);
}
pub fn security_level(&self) -> FipsSecurityLevel {
self.security_level
}
pub fn self_test_status(&self) -> bool {
self.self_test_passed
}
}
#[derive(Zeroize)]
#[zeroize(drop)]
pub struct SecureKey {
key_material: Vec<u8>,
}
impl SecureKey {
pub fn new(key_material: Vec<u8>) -> Self {
SecureKey { key_material }
}
pub fn as_bytes(&self) -> &[u8] {
&self.key_material
}
}
pub struct FipsHash;
impl FipsHash {
pub fn sha512(data: &[u8]) -> Vec<u8> {
let mut hasher = Sha512::new();
hasher.update(data);
hasher.finalize().to_vec()
}
pub fn sha3_512(data: &[u8]) -> Vec<u8> {
let mut hasher = Sha3_512::new();
hasher.update(data);
hasher.finalize().to_vec()
}
pub fn sha256(data: &[u8]) -> Vec<u8> {
let mut hasher = Sha256::new();
hasher.update(data);
hasher.finalize().to_vec()
}
}
pub struct FipsHmac;
impl FipsHmac {
pub fn hmac_sha512(key: &[u8], data: &[u8]) -> Result<Vec<u8>, String> {
type HmacSha512 = Hmac<Sha512>;
let mut mac =
HmacSha512::new_from_slice(key).map_err(|_| "HMAC key initialization failed")?;
mac.update(data);
Ok(mac.finalize().into_bytes().to_vec())
}
pub fn verify_hmac_sha512(key: &[u8], data: &[u8], tag: &[u8]) -> Result<(), String> {
type HmacSha512 = Hmac<Sha512>;
let mut mac =
HmacSha512::new_from_slice(key).map_err(|_| "HMAC key initialization failed")?;
mac.update(data);
mac.verify_slice(tag)
.map_err(|_| "HMAC verification failed".to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fips_power_on_self_test() {
let mut module = FipsModule::new();
assert!(module.power_on_self_test().is_ok());
assert!(module.self_test_status());
}
#[test]
fn test_sha256_kat() {
let module = FipsModule::new();
assert!(module.test_sha256().is_ok());
}
#[test]
fn test_sha512_kat() {
let module = FipsModule::new();
assert!(module.test_sha512().is_ok());
}
#[test]
fn test_sha3_512_kat() {
let module = FipsModule::new();
assert!(module.test_sha3_512().is_ok());
}
#[test]
fn test_hmac_sha256_kat() {
let module = FipsModule::new();
assert!(module.test_hmac_sha256().is_ok());
}
#[test]
fn test_secure_key_zeroization() {
let key = SecureKey::new(vec![1, 2, 3, 4, 5]);
assert_eq!(key.as_bytes(), &[1, 2, 3, 4, 5]);
drop(key);
}
#[test]
fn test_fips_hash_sha512() {
let data = b"test data";
let hash = FipsHash::sha512(data);
assert_eq!(hash.len(), 64); }
#[test]
fn test_fips_hmac() {
let key = b"secret key";
let data = b"message";
let tag = FipsHmac::hmac_sha512(key, data).unwrap();
assert!(FipsHmac::verify_hmac_sha512(key, data, &tag).is_ok());
}
}