entropy.rs (1951B)
1 //! Injected entropy contract and production operating-system adapter. 2 3 use core::fmt; 4 use std::error::Error; 5 6 /// Failure to obtain cryptographically secure host entropy. 7 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 8 pub enum EntropyError { 9 Unavailable, 10 } 11 12 impl fmt::Display for EntropyError { 13 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 14 formatter.write_str("host entropy is unavailable") 15 } 16 } 17 18 impl Error for EntropyError {} 19 20 /// Injected source of cryptographically secure bytes. 21 pub trait EntropySource: Send + Sync { 22 /// Fills the complete destination or returns an error without partial success. 23 fn fill_bytes(&self, destination: &mut [u8]) -> Result<(), EntropyError>; 24 } 25 26 /// Production entropy source backed by the operating system. 27 #[derive(Clone, Copy, Debug, Default)] 28 pub struct SystemEntropy; 29 30 impl EntropySource for SystemEntropy { 31 fn fill_bytes(&self, destination: &mut [u8]) -> Result<(), EntropyError> { 32 getrandom::getrandom(destination).map_err(|_| EntropyError::Unavailable) 33 } 34 } 35 36 #[cfg(test)] 37 mod tests { 38 use super::*; 39 40 struct FailingEntropy; 41 42 impl EntropySource for FailingEntropy { 43 fn fill_bytes(&self, _destination: &mut [u8]) -> Result<(), EntropyError> { 44 Err(EntropyError::Unavailable) 45 } 46 } 47 48 #[test] 49 fn injected_entropy_errors_are_typed_and_safe() { 50 let mut destination = [0_u8; 16]; 51 let error = FailingEntropy 52 .fill_bytes(&mut destination) 53 .expect_err("entropy failure"); 54 55 assert_eq!(error, EntropyError::Unavailable); 56 assert_eq!(error.to_string(), "host entropy is unavailable"); 57 assert_eq!(destination, [0; 16]); 58 } 59 60 #[test] 61 fn production_entropy_adapter_smoke_test() { 62 let mut destination = [0_u8; 32]; 63 SystemEntropy 64 .fill_bytes(&mut destination) 65 .expect("operating-system entropy"); 66 } 67 }