use crate::core::memory::zero_secure;
pub struct SecureBuffer<'a> {
bytes: Option<&'a mut [u8]>,
locked: bool,
}
impl<'a> SecureBuffer<'a> {
#[must_use]
pub fn new(bytes: &'a mut [u8]) -> Self {
#[cfg(target_os = "linux")]
let locked = if bytes.is_empty() {
false
} else {
unsafe { libc::mlock(bytes.as_ptr().cast(), bytes.len()) == 0 }
};
#[cfg(not(target_os = "linux"))]
let locked = false;
Self {
bytes: Some(bytes),
locked,
}
}
#[must_use]
pub fn as_slice(&self) -> &[u8] {
self.bytes.as_deref().unwrap_or_default()
}
#[must_use]
pub fn as_mut_slice(&mut self) -> &mut [u8] {
self.bytes.as_deref_mut().unwrap_or_default()
}
#[must_use]
pub fn size_bytes(&self) -> usize {
self.bytes.as_deref().map_or(0, <[u8]>::len)
}
#[must_use]
pub fn is_valid(&self) -> bool {
self.size_bytes() != 0
}
#[must_use]
pub const fn is_locked(&self) -> bool {
self.locked
}
pub fn secure_zero(&mut self) {
if let Some(bytes) = self.bytes.as_deref_mut() {
zero_secure(bytes);
}
}
pub fn reset(&mut self) {
self.secure_zero();
if let Some(bytes) = self.bytes.take() {
#[cfg(target_os = "linux")]
if self.locked {
unsafe {
libc::munlock(bytes.as_ptr().cast(), bytes.len());
}
}
}
self.locked = false;
}
}
impl Drop for SecureBuffer<'_> {
fn drop(&mut self) {
self.reset();
}
}
impl std::fmt::Debug for SecureBuffer<'_> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("SecureBuffer")
.field("size_bytes", &self.size_bytes())
.field("locked", &self.locked)
.finish_non_exhaustive()
}
}