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}