libsodium_rs/utils/vec_utils.rs
1//! # Secure Vector Utilities Module
2//!
3//! This module provides secure memory management utilities for vectors and slices,
4//! designed specifically for handling sensitive cryptographic material.
5//!
6//! ## Key Features
7//!
8//! * **Memory Zeroing**: Securely clear sensitive data from memory
9//! * **Memory Locking**: Prevent sensitive data from being swapped to disk
10//! * **Secure Vectors**: A vector-like container with enhanced memory protection
11//!
12//! ## Security Considerations
13//!
14//! When working with cryptographic keys, passwords, or other sensitive data, it's
15//! crucial to handle memory securely. This module provides tools to:
16//!
17//! * Prevent sensitive data from being written to disk via swap
18//! * Ensure memory is properly zeroed when no longer needed
19//! * Protect against memory-related vulnerabilities
20//!
21//! ## Usage Example
22//!
23//! ```rust
24//! use libsodium_rs as sodium;
25//! use libsodium_rs::utils::vec_utils;
26//! use sodium::ensure_init;
27//!
28//! fn main() -> Result<(), Box<dyn std::error::Error>> {
29//! ensure_init()?;
30//!
31//! // Create a secure vector for a cryptographic key
32//! let mut secure_key = vec_utils::secure_vec::<u8>(32)?;
33//!
34//! // Fill it with random data or your key material
35//! for i in 0..secure_key.len() {
36//! secure_key[i] = i as u8;
37//! }
38//!
39//! // Use the key for cryptographic operations...
40//!
41//! // When secure_key goes out of scope, the memory is
42//! // automatically zeroed and freed
43//! Ok(())
44//! }
45//! ```
46//!
47//! ## Relationship to Other Modules
48//!
49//! This module is part of the `utils` module and complements the core memory
50//! management functions with vector-specific utilities.
51
52use std::io;
53use std::ops::{Deref, DerefMut};
54use std::ptr;
55
56/// Securely zero a slice's memory
57///
58/// This function securely zeroes a slice's memory, ensuring that the operation
59/// won't be optimized out by the compiler. This is important for securely
60/// clearing sensitive data from memory.
61///
62/// ## Security Considerations
63///
64/// - This function ensures that the memory is actually zeroed, even if the compiler
65/// would normally optimize out the operation
66/// - It should be used whenever a slice containing sensitive data (like cryptographic
67/// keys or passwords) is no longer needed
68/// - Regular assignment (e.g., `slice.iter_mut().for_each(|x| *x = T::default())`) might
69/// be optimized out by the compiler and not actually clear the memory
70///
71/// ## Example
72///
73/// ```rust
74/// use libsodium_rs as sodium;
75/// use libsodium_rs::utils::vec_utils;
76///
77/// // Create a slice with sensitive data
78/// let mut secret_key = [0x01, 0x02, 0x03, 0x04];
79///
80/// // Use the key for some operation...
81///
82/// // Securely clear the key from memory when done
83/// vec_utils::memzero(&mut secret_key);
84/// assert_eq!(secret_key, [0, 0, 0, 0]);
85/// ```
86///
87/// # Arguments
88/// * `slice` - The slice to zero
89pub fn memzero<T: Default + Clone>(slice: &mut [T]) {
90 for item in slice.iter_mut() {
91 *item = T::default();
92 }
93}
94
95/// Lock a vector's memory to prevent it from being swapped to disk
96///
97/// This function locks the memory pages containing the provided vector, preventing
98/// them from being swapped to disk. This is important for protecting sensitive
99/// cryptographic material from being written to disk where it might be recovered later.
100///
101/// ## Security Considerations
102///
103/// - Locked memory is not swapped to disk, reducing the risk of sensitive data leakage
104/// - This function should be used for vectors containing highly sensitive data like
105/// cryptographic keys or passwords
106/// - Remember to call `munlock` when the vector is no longer needed
107/// - There may be system-wide limits on the amount of memory that can be locked
108///
109/// ## Example
110///
111/// ```rust
112/// use libsodium_rs as sodium;
113/// use libsodium_rs::utils::vec_utils;
114///
115/// let mut sensitive_data = vec![0x01, 0x02, 0x03, 0x04];
116/// vec_utils::mlock(&mut sensitive_data).expect("Failed to lock memory");
117/// // Use the sensitive data...
118/// vec_utils::munlock(&mut sensitive_data).expect("Failed to unlock memory");
119/// ```
120///
121/// # Arguments
122/// * `vec` - The vector to lock
123///
124/// # Returns
125/// * `io::Result<()>` - Success or an error if the memory couldn't be locked
126pub fn mlock<T>(vec: &mut Vec<T>) -> io::Result<()> {
127 super::mlock(unsafe {
128 std::slice::from_raw_parts_mut(
129 vec.as_mut_ptr() as *mut u8,
130 vec.len() * std::mem::size_of::<T>(),
131 )
132 })
133}
134
135/// Unlock a previously locked vector's memory
136///
137/// This function unlocks memory pages that were previously locked with `mlock`.
138/// It should be called when the sensitive data is no longer needed.
139///
140/// ## Example
141///
142/// ```rust
143/// use libsodium_rs as sodium;
144/// use libsodium_rs::utils::vec_utils;
145///
146/// let mut sensitive_data = vec![0x01, 0x02, 0x03, 0x04];
147/// vec_utils::mlock(&mut sensitive_data).expect("Failed to lock memory");
148/// // Use the sensitive data...
149/// vec_utils::munlock(&mut sensitive_data).expect("Failed to unlock memory");
150/// ```
151///
152/// # Arguments
153/// * `vec` - The vector to unlock
154///
155/// # Returns
156/// * `io::Result<()>` - Success or an error if the memory couldn't be unlocked
157pub fn munlock<T>(vec: &mut Vec<T>) -> io::Result<()> {
158 super::munlock(unsafe {
159 std::slice::from_raw_parts_mut(
160 vec.as_mut_ptr() as *mut u8,
161 vec.len() * std::mem::size_of::<T>(),
162 )
163 })
164}
165
166/// Create a new secure vector with enhanced memory protection
167///
168/// This function creates a new `SecureVec<T>` with comprehensive memory protection features
169/// designed for storing sensitive cryptographic material.
170///
171/// ## Security Features
172///
173/// - **Secure Allocation**: Uses libsodium's `sodium_malloc()` for memory allocation with guard pages
174/// - **Overflow Detection**: Canary values and guard pages detect buffer overflows and underflows
175/// - **Memory Locking**: Allocated memory is locked to prevent it from being swapped to disk
176/// - **Automatic Zeroing**: Memory is automatically and securely zeroed when freed
177/// - **Use-after-free Protection**: Helps mitigate use-after-free vulnerabilities
178///
179/// ## Performance Considerations
180///
181/// - Secure memory allocation has higher overhead than standard allocation
182/// - Memory is page-aligned, which may use more memory than strictly necessary
183/// - The memory locking feature may be subject to system-wide limits
184///
185/// ## Error Handling
186///
187/// This function returns an `io::Result<SecureVec<T>>` which will be an error if:
188/// - The system has insufficient memory
189/// - The process has reached its limit for locked memory
190/// - The secure memory allocation fails for any other reason
191///
192/// ## Example
193///
194/// ```rust
195/// use libsodium_rs as sodium;
196/// use libsodium_rs::utils::vec_utils;
197/// use sodium::ensure_init;
198///
199/// fn main() -> Result<(), Box<dyn std::error::Error>> {
200/// ensure_init()?;
201///
202/// // Create a secure vector
203/// let mut secure_vec = vec_utils::secure_vec::<u8>(32)?;
204///
205/// // Use it like a regular vector
206/// for i in 0..secure_vec.len() {
207/// secure_vec[i] = i as u8;
208/// }
209///
210/// // When it goes out of scope, memory is automatically zeroed and freed
211/// Ok(())
212/// }
213/// ```
214///
215/// # Arguments
216/// * `size` - The initial size of the vector
217///
218/// # Returns
219/// * `io::Result<SecureVec<T>>` - A new secure vector or an error if allocation failed
220pub fn secure_vec<T: Default + Clone>(size: usize) -> io::Result<SecureVec<T>> {
221 let mem_size = size * std::mem::size_of::<T>();
222 let ptr = super::malloc(mem_size) as *mut T;
223
224 if ptr.is_null() {
225 return Err(io::Error::new(
226 io::ErrorKind::Other,
227 "Failed to allocate secure memory",
228 ));
229 }
230
231 // Initialize the memory with default values
232 for i in 0..size {
233 unsafe {
234 ptr.add(i).write(T::default());
235 }
236 }
237
238 Ok(SecureVec {
239 ptr,
240 len: size,
241 capacity: size,
242 _marker: std::marker::PhantomData,
243 })
244}
245
246/// A secure vector with enhanced memory protection
247///
248/// `SecureVec<T>` is a vector-like container that provides comprehensive memory protection
249/// for sensitive cryptographic material. It combines the ergonomics of Rust's `Vec<T>` with
250/// the security features of libsodium's secure memory allocation functions.
251///
252/// ## Security Features
253///
254/// - **Canary-based Protection**: Detects buffer overflows and underflows using guard pages and canary values
255/// - **Automatic Zeroing**: Memory is automatically and securely zeroed when freed
256/// - **Memory Locking**: Memory is locked to prevent it from being swapped to disk
257/// - **Use-after-free Protection**: Helps prevent use-after-free vulnerabilities
258/// - **Overflow Detection**: Uses guarded pages to detect and prevent buffer overflows
259///
260/// ## Implementation Details
261///
262/// Unlike a standard Rust `Vec<T>`, `SecureVec<T>` uses libsodium's `sodium_malloc()` and
263/// `sodium_free()` functions to allocate and free memory. These functions provide additional
264/// security features beyond what standard memory allocation provides:
265///
266/// - Memory is allocated with guard pages before and after the requested region
267/// - Canary values are placed at the boundaries to detect overflows/underflows
268/// - Memory is automatically zeroed when freed
269/// - The allocated memory is page-aligned and protected from being swapped to disk
270///
271/// ## Usage
272///
273/// `SecureVec<T>` implements `Deref` and `DerefMut` to `[T]`, allowing it to be used
274/// like a standard slice. It also provides methods similar to `Vec<T>` such as `push()`,
275/// `pop()`, and `clear()`.
276///
277/// ```rust
278/// use libsodium_rs as sodium;
279/// use libsodium_rs::utils::vec_utils;
280/// use sodium::ensure_init;
281///
282/// fn main() -> Result<(), Box<dyn std::error::Error>> {
283/// ensure_init()?;
284///
285/// // Create a secure vector
286/// let mut secure_vec = vec_utils::secure_vec::<u8>(32)?;
287///
288/// // Use it like a regular vector
289/// for i in 0..secure_vec.len() {
290/// secure_vec[i] = i as u8;
291/// }
292///
293/// // When it goes out of scope, memory is automatically zeroed and freed
294/// Ok(())
295/// }
296/// ```
297pub struct SecureVec<T: Default + Clone> {
298 // Raw pointer to the allocated memory
299 ptr: *mut T,
300 // Current length of the vector
301 len: usize,
302 // Current capacity of the vector
303 capacity: usize,
304 // Phantom data to indicate ownership of T
305 _marker: std::marker::PhantomData<T>,
306}
307
308impl<T: Default + Clone> SecureVec<T> {
309 /// Returns the length of the vector
310 ///
311 /// This method returns the number of elements in the vector.
312 ///
313 /// ## Example
314 ///
315 /// ```rust
316 /// use libsodium_rs as sodium;
317 /// use libsodium_rs::utils::vec_utils;
318 /// use sodium::ensure_init;
319 ///
320 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
321 /// ensure_init()?;
322 ///
323 /// let secure_vec = vec_utils::secure_vec::<u8>(32)?;
324 /// assert_eq!(secure_vec.len(), 32);
325 ///
326 /// Ok(())
327 /// }
328 /// ```
329 pub fn len(&self) -> usize {
330 self.len
331 }
332
333 /// Returns true if the vector is empty
334 ///
335 /// This method returns true if the vector contains no elements.
336 ///
337 /// ## Example
338 ///
339 /// ```rust
340 /// use libsodium_rs as sodium;
341 /// use libsodium_rs::utils::vec_utils;
342 /// use sodium::ensure_init;
343 ///
344 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
345 /// ensure_init()?;
346 ///
347 /// let secure_vec = vec_utils::secure_vec::<u8>(0)?;
348 /// assert!(secure_vec.is_empty());
349 ///
350 /// Ok(())
351 /// }
352 /// ```
353 pub fn is_empty(&self) -> bool {
354 self.len == 0
355 }
356
357 /// Convert the secure vector into a standard Vec
358 ///
359 /// This function consumes the `SecureVec<T>` and returns a standard `Vec<T>` with the same contents.
360 /// The returned vector will no longer have the enhanced memory protections that `SecureVec<T>` provides.
361 ///
362 /// ## Security Considerations
363 ///
364 /// - **Loss of Protection**: After conversion, the memory loses all the security features provided by `SecureVec<T>`
365 /// - **Caller Responsibility**: The caller becomes responsible for securely handling the returned vector
366 /// - **Recommended Practice**: Consider using `memzero()` on the returned vector when it's no longer needed
367 /// - **Swapping Risk**: The memory in the returned vector may be swapped to disk, potentially exposing sensitive data
368 ///
369 /// ## Example
370 ///
371 /// ```rust
372 /// use libsodium_rs as sodium;
373 /// use libsodium_rs::utils::vec_utils;
374 /// use sodium::ensure_init;
375 ///
376 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
377 /// ensure_init()?;
378 ///
379 /// // Create a secure vector
380 /// let mut secure_vec = vec_utils::secure_vec::<u8>(4)?;
381 /// for i in 0..secure_vec.len() {
382 /// secure_vec[i] = i as u8 + 1;
383 /// }
384 ///
385 /// // Convert to a standard vector (losing security protections)
386 /// let mut regular_vec = secure_vec.into_vec();
387 ///
388 /// // Use the regular vector...
389 ///
390 /// // Manually zero the memory when done
391 /// vec_utils::memzero(&mut regular_vec);
392 ///
393 /// Ok(())
394 /// }
395 /// ```
396 pub fn into_vec(self) -> Vec<T> {
397 let mut vec = Vec::with_capacity(self.len);
398
399 // Copy the elements from the secure vector to the standard vector
400 for i in 0..self.len {
401 unsafe {
402 vec.push(ptr::read(self.ptr.add(i)));
403 }
404 }
405
406 // Prevent the destructor from running
407 std::mem::forget(self);
408
409 vec
410 }
411
412 /// Clears the vector, removing all values
413 ///
414 /// This method clears the vector, setting its length to 0 but keeping the allocated memory.
415 /// All elements are securely zeroed.
416 ///
417 /// ## Example
418 ///
419 /// ```rust
420 /// use libsodium_rs as sodium;
421 /// use libsodium_rs::utils::vec_utils;
422 /// use sodium::ensure_init;
423 ///
424 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
425 /// ensure_init()?;
426 ///
427 /// let mut secure_vec = vec_utils::secure_vec::<u8>(4)?;
428 /// for i in 0..secure_vec.len() {
429 /// secure_vec[i] = i as u8 + 1;
430 /// }
431 ///
432 /// secure_vec.clear();
433 /// assert_eq!(secure_vec.len(), 0);
434 ///
435 /// Ok(())
436 /// }
437 /// ```
438 pub fn clear(&mut self) {
439 // Zero all elements
440 for i in 0..self.len {
441 unsafe {
442 self.ptr.add(i).write(T::default());
443 }
444 }
445
446 // Set the length to 0
447 self.len = 0;
448 }
449
450 /// Adds an element to the end of the vector
451 ///
452 /// This method adds an element to the end of the vector, increasing its length by 1.
453 /// If the vector's capacity is too small, it will be reallocated with a larger capacity.
454 ///
455 /// ## Example
456 ///
457 /// ```rust
458 /// use libsodium_rs as sodium;
459 /// use libsodium_rs::utils::vec_utils;
460 /// use sodium::ensure_init;
461 ///
462 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
463 /// ensure_init()?;
464 ///
465 /// let mut secure_vec = vec_utils::secure_vec::<u8>(0)?;
466 /// secure_vec.push(1);
467 /// secure_vec.push(2);
468 /// secure_vec.push(3);
469 ///
470 /// assert_eq!(secure_vec.len(), 3);
471 /// assert_eq!(secure_vec[0], 1);
472 /// assert_eq!(secure_vec[1], 2);
473 /// assert_eq!(secure_vec[2], 3);
474 ///
475 /// Ok(())
476 /// }
477 /// ```
478 pub fn push(&mut self, value: T) -> io::Result<()> {
479 if self.len == self.capacity {
480 // Double the capacity, with a minimum of 1
481 let new_capacity = std::cmp::max(1, self.capacity * 2);
482 self.reserve(new_capacity - self.capacity)?;
483 }
484
485 unsafe {
486 self.ptr.add(self.len).write(value);
487 }
488
489 self.len += 1;
490 Ok(())
491 }
492
493 /// Removes the last element from the vector and returns it
494 ///
495 /// This method removes the last element from the vector and returns it.
496 /// If the vector is empty, None is returned.
497 ///
498 /// ## Example
499 ///
500 /// ```rust
501 /// use libsodium_rs as sodium;
502 /// use libsodium_rs::utils::vec_utils;
503 /// use sodium::ensure_init;
504 ///
505 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
506 /// ensure_init()?;
507 ///
508 /// let mut secure_vec = vec_utils::secure_vec::<u8>(0)?;
509 /// secure_vec.push(1)?;
510 /// secure_vec.push(2)?;
511 ///
512 /// assert_eq!(secure_vec.pop(), Some(2));
513 /// assert_eq!(secure_vec.pop(), Some(1));
514 /// assert_eq!(secure_vec.pop(), None);
515 ///
516 /// Ok(())
517 /// }
518 /// ```
519 pub fn pop(&mut self) -> Option<T> {
520 if self.len == 0 {
521 return None;
522 }
523
524 self.len -= 1;
525 let value = unsafe { ptr::read(self.ptr.add(self.len)) };
526
527 // Zero the memory that contained the popped value
528 unsafe {
529 self.ptr.add(self.len).write(T::default());
530 }
531
532 Some(value)
533 }
534
535 /// Reserves capacity for at least `additional` more elements
536 ///
537 /// This method reserves capacity for at least `additional` more elements to be inserted
538 /// into the vector. The collection may reserve more space to avoid frequent reallocations.
539 /// After calling `reserve`, the capacity will be greater than or equal to `self.len() + additional`.
540 ///
541 /// ## Example
542 ///
543 /// ```rust
544 /// use libsodium_rs as sodium;
545 /// use libsodium_rs::utils::vec_utils;
546 /// use sodium::ensure_init;
547 ///
548 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
549 /// ensure_init()?;
550 ///
551 /// let mut secure_vec = vec_utils::secure_vec::<u8>(0)?;
552 /// secure_vec.reserve(10)?;
553 ///
554 /// // Now we can add up to 10 elements without reallocating
555 /// for i in 0..10 {
556 /// secure_vec.push(i)?;
557 /// }
558 ///
559 /// assert_eq!(secure_vec.len(), 10);
560 ///
561 /// Ok(())
562 /// }
563 /// ```
564 pub fn reserve(&mut self, additional: usize) -> io::Result<()> {
565 if additional == 0 {
566 return Ok(());
567 }
568
569 let new_capacity = self.capacity + additional;
570 let new_mem_size = new_capacity * std::mem::size_of::<T>();
571 let new_ptr = super::malloc(new_mem_size) as *mut T;
572
573 if new_ptr.is_null() {
574 return Err(io::Error::new(
575 io::ErrorKind::Other,
576 "Failed to allocate secure memory",
577 ));
578 }
579
580 // Copy existing elements to the new memory
581 for i in 0..self.len {
582 unsafe {
583 new_ptr.add(i).write(ptr::read(self.ptr.add(i)));
584 }
585 }
586
587 // Initialize the new elements with default values
588 for i in self.len..new_capacity {
589 unsafe {
590 new_ptr.add(i).write(T::default());
591 }
592 }
593
594 // Free the old memory
595 unsafe {
596 super::free(self.ptr as *mut libc::c_void);
597 }
598
599 self.ptr = new_ptr;
600 self.capacity = new_capacity;
601
602 Ok(())
603 }
604}
605
606impl<T: Default + Clone> Deref for SecureVec<T> {
607 type Target = [T];
608
609 fn deref(&self) -> &Self::Target {
610 unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
611 }
612}
613
614impl<T: Default + Clone> DerefMut for SecureVec<T> {
615 fn deref_mut(&mut self) -> &mut Self::Target {
616 unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len) }
617 }
618}
619
620impl<T: Default + Clone> Drop for SecureVec<T> {
621 fn drop(&mut self) {
622 // Zero all elements
623 for i in 0..self.len {
624 unsafe {
625 self.ptr.add(i).write(T::default());
626 }
627 }
628
629 // Free the memory
630 unsafe {
631 super::free(self.ptr as *mut libc::c_void);
632 }
633 }
634}
635
636impl<T: Default + Clone> Clone for SecureVec<T> {
637 fn clone(&self) -> Self {
638 let mem_size = self.capacity * std::mem::size_of::<T>();
639 let ptr = super::malloc(mem_size) as *mut T;
640
641 if ptr.is_null() {
642 panic!("Failed to allocate secure memory");
643 }
644
645 // Copy elements to the new memory
646 for i in 0..self.len {
647 unsafe {
648 ptr.add(i).write((*self)[i].clone());
649 }
650 }
651
652 // Initialize the remaining elements with default values
653 for i in self.len..self.capacity {
654 unsafe {
655 ptr.add(i).write(T::default());
656 }
657 }
658
659 SecureVec {
660 ptr,
661 len: self.len,
662 capacity: self.capacity,
663 _marker: std::marker::PhantomData,
664 }
665 }
666}
667
668// Implement Debug for SecureVec
669impl<T: Default + Clone + std::fmt::Debug> std::fmt::Debug for SecureVec<T> {
670 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
671 f.debug_list().entries(self.iter()).finish()
672 }
673}
674
675// Implement PartialEq for SecureVec
676impl<T: Default + Clone + PartialEq> PartialEq for SecureVec<T> {
677 fn eq(&self, other: &Self) -> bool {
678 if self.len != other.len {
679 return false;
680 }
681
682 for i in 0..self.len {
683 if self[i] != other[i] {
684 return false;
685 }
686 }
687
688 true
689 }
690}
691
692// Implement Eq for SecureVec if T implements Eq
693impl<T: Default + Clone + Eq> Eq for SecureVec<T> {}
694
695// Implement PartialEq<[T]> for SecureVec
696impl<T: Default + Clone + PartialEq> PartialEq<[T]> for SecureVec<T> {
697 fn eq(&self, other: &[T]) -> bool {
698 if self.len != other.len() {
699 return false;
700 }
701
702 for i in 0..self.len {
703 if self[i] != other[i] {
704 return false;
705 }
706 }
707
708 true
709 }
710}
711
712// Implement PartialEq<Vec<T>> for SecureVec
713impl<T: Default + Clone + PartialEq> PartialEq<Vec<T>> for SecureVec<T> {
714 fn eq(&self, other: &Vec<T>) -> bool {
715 if self.len != other.len() {
716 return false;
717 }
718
719 for i in 0..self.len {
720 if self[i] != other[i] {
721 return false;
722 }
723 }
724
725 true
726 }
727}
728
729// Implement PartialEq<SecureVec<T>> for [T]
730impl<T: Default + Clone + PartialEq> PartialEq<SecureVec<T>> for [T] {
731 fn eq(&self, other: &SecureVec<T>) -> bool {
732 other == self
733 }
734}
735
736// Implement PartialEq<SecureVec<T>> for Vec<T>
737impl<T: Default + Clone + PartialEq> PartialEq<SecureVec<T>> for Vec<T> {
738 fn eq(&self, other: &SecureVec<T>) -> bool {
739 other == self
740 }
741}