pub struct SecureArray<T, const LENGTH: usize>where
T: Zeroize,{ /* private fields */ }Expand description
A fixed-size array allocated in a secure memory region.
§Security Model
When compiled with the use_os feature (the default), it provides several layers of protection:
- Zeroization on Drop: The memory is zeroized when the array is dropped.
- Memory Locking: The underlying memory pages are locked using
mlock&madvisefor (Unix) orVirtualLock&VirtualProtectfor (Windows) to prevent the OS from memory-dump/swap to disk or other processes accessing the memory.
In a no_std environment, it falls back to providing only the zeroization-on-drop guarantee.
§Security Note
We intentionally do not implement Index or IndexMut.
array[0] is a compile error.
Always use .unlock() / .unlock_mut() (or the slice variants) to access data.
§Thread Safety
SecureArray is Send (it can be moved to another thread) but not Sync.
unlock / unlock_mut change the allocation’s page protection, so two threads
unlocking the same instance would race (one can relock while the other still
holds a live slice). Share it as Arc<Mutex<SecureArray<...>>>.
§Notes
If you return a new allocated [T; LENGTH] from one of the unlock methods you are responsible for zeroizing the memory.
§Example
use secure_types::{SecureArray, Zeroize};
let exposed_key: &mut [u8; 32] = &mut [1u8; 32];
let secure_key: SecureArray<u8, 32> = SecureArray::from_slice_mut(exposed_key).unwrap();
secure_key.unlock(|unlocked_slice| {
assert_eq!(unlocked_slice.len(), 32);
assert_eq!(unlocked_slice[0], 1);
});
// Not recommended but if you allocate a new [u8; LENGTH] make sure to zeroize it
let mut exposed = secure_key.unlock(|unlocked_slice| {
[unlocked_slice[0], unlocked_slice[1], unlocked_slice[2]]
});
// Do what you need to to do with the new array
// When you are done with it, zeroize it
exposed.zeroize();Implementations§
Source§impl<T, const LENGTH: usize> SecureArray<T, LENGTH>where
T: Zeroize,
impl<T, const LENGTH: usize> SecureArray<T, LENGTH>where
T: Zeroize,
Sourcepub fn empty() -> Result<Self, Error>
pub fn empty() -> Result<Self, Error>
Creates an empty (but allocated) SecureArray.
The memory is allocated but not initialized, and it’s the caller’s responsibility to fill it.
Only elements that are actually written are tracked as initialized: drop
and erase zeroize just those, so dropping an array that was never filled is
sound. The remaining slots still hold the allocator’s poison bytes and must
never be read as a T. Initialize the whole array (for example through
unlock_mut) before accessing it.
Sourcepub fn from_slice_mut(content: &mut [T; LENGTH]) -> Result<Self, Error>where
T: Clone,
pub fn from_slice_mut(content: &mut [T; LENGTH]) -> Result<Self, Error>where
T: Clone,
Creates a new SecureArray from a &mut [T; LENGTH].
The passed slice is zeroized afterwards
Sourcepub fn from_slice(content: &[T; LENGTH]) -> Result<Self, Error>where
T: Clone,
pub fn from_slice(content: &[T; LENGTH]) -> Result<Self, Error>where
T: Clone,
Creates a new SecureArray from a &[T; LENGTH].
The array is not zeroized, you are responsible for zeroizing it
pub fn len(&self) -> usize
pub fn is_empty(&self) -> bool
Sourcepub fn unlock<F, R>(&self, f: F) -> R
pub fn unlock<F, R>(&self, f: F) -> R
Immutable access to the array’s data as a &[T]
The slice covers exactly the elements that have been initialized: LENGTH
for any array built through a constructor or unlock_mut, and empty for
an empty array that was never filled (see its contract).
Sourcepub fn unlock_mut<F, R>(&mut self, f: F) -> R
pub fn unlock_mut<F, R>(&mut self, f: F) -> R
Mutable access to the array’s data as a &mut [T]
Exposing the whole array as a &mut [T] treats every slot as initialized
storage, so a later drop / erase zeroizes all LENGTH elements.
Trait Implementations§
Source§impl<T: Clone + Zeroize, const LENGTH: usize> From<SecureArray<T, LENGTH>> for SecureVec<T>
impl<T: Clone + Zeroize, const LENGTH: usize> From<SecureArray<T, LENGTH>> for SecureVec<T>
Source§fn from(array: SecureArray<T, LENGTH>) -> Self
fn from(array: SecureArray<T, LENGTH>) -> Self
§Panics
Panics if the new secure allocation cannot be made or locked.
impl<T: Zeroize + Send, const LENGTH: usize> Send for SecureArray<T, LENGTH>
Source§impl<T: Clone + Zeroize, const LENGTH: usize> TryFrom<SecureVec<T>> for SecureArray<T, LENGTH>
impl<T: Clone + Zeroize, const LENGTH: usize> TryFrom<SecureVec<T>> for SecureArray<T, LENGTH>
Source§fn try_from(vec: SecureVec<T>) -> Result<Self, Self::Error>
fn try_from(vec: SecureVec<T>) -> Result<Self, Self::Error>
Tries to convert a SecureVec<T> into a SecureArray<T, LENGTH>.
This operation will only succeed if vec.len() == LENGTH.
LENGTH is a compile-time constant on the destination type, it cannot
be taken from the vector’s runtime length.
The SecureVec is consumed.
Source§impl<T: Clone + Zeroize, const LENGTH: usize> TryFrom<Vec<T>> for SecureArray<T, LENGTH>
impl<T: Clone + Zeroize, const LENGTH: usize> TryFrom<Vec<T>> for SecureArray<T, LENGTH>
Source§fn try_from(vec: Vec<T>) -> Result<Self, Self::Error>
fn try_from(vec: Vec<T>) -> Result<Self, Self::Error>
Tries to convert a Vec<T> into a SecureArray<T, LENGTH>.
This operation will only succeed if vec.len() == LENGTH.
LENGTH is a compile-time constant on the destination type, it cannot
be taken from the vector’s runtime length.
The Vec is consumed and zeroized.