use memmap2::MmapMut;
use crate::{Error, Result, core::memory::zero_secure};
pub(crate) struct Entropy<const N: usize> {
storage: MmapMut,
}
impl<const N: usize> Entropy<N> {
pub(crate) fn generate() -> Result<Self> {
Self::from_source(|bytes| {
getrandom::fill(bytes)
.map_err(|_| Error::internal("operating-system entropy generation failed"))
})
}
fn from_source(source: impl FnOnce(&mut [u8]) -> Result<()>) -> Result<Self> {
if N != 32 && N != 64 {
return Err(Error::invalid_argument(
"PQC entropy must contain 32 or 64 bytes",
));
}
#[cfg(not(unix))]
{
let _ = source;
Err(Error::missing_capability(
"locked host entropy requires a supported Unix adapter",
))
}
#[cfg(unix)]
{
let storage = MmapMut::map_anon(N)
.map_err(|e| Error::backend_failure("host entropy", "allocation", e))?;
storage
.lock()
.map_err(|e| Error::backend_failure("host entropy", "page locking", e))?;
let mut entropy = Self { storage };
source(&mut entropy.storage)?;
Ok(entropy)
}
}
pub(crate) fn consume<T>(mut self, consumer: impl FnOnce(&[u8]) -> Result<T>) -> Result<T> {
consume_bytes(&mut self.storage, consumer)
}
}
fn consume_bytes<T>(bytes: &mut [u8], consumer: impl FnOnce(&[u8]) -> Result<T>) -> Result<T> {
let guard = EraseOnDrop(bytes);
consumer(guard.0)
}
struct EraseOnDrop<'a>(&'a mut [u8]);
impl Drop for EraseOnDrop<'_> {
fn drop(&mut self) {
zero_secure(self.0);
}
}
impl<const N: usize> Drop for Entropy<N> {
fn drop(&mut self) {
zero_secure(&mut self.storage);
}
}
#[cfg(test)]
#[path = "../../../test/rs/cryptography/entropy_unit.rs"]
mod tests;