secure_types/
lib.rs

1//! # Secure Types
2//!
3//! This crate provides heap-allocated data structures (`SecureVec`, `SecureArray`, `SecureString`)
4//! designed to handle sensitive information in memory with enhanced security.
5//!
6//! ## Core Security Guarantees
7//!
8//! The primary goal is to protect secret data (like passwords, private keys, or credentials)
9//! from being exposed through common vulnerabilities.
10//!
11//! 1.  **Zeroization on Drop**: All secure types implement the `Zeroize` trait, ensuring their
12//!     memory is securely overwritten with zeros when they are dropped. This prevents stale
13//!     data from being recoverable in deallocated memory.
14//!
15//! 2.  **Memory Locking (`std` only)**: When compiled with the `std` feature (the default),
16//!     the crate uses OS-level primitives to lock memory pages, preventing them from being
17//!     swapped to disk.
18//!     - On Windows: `VirtualLock` and `VirtualProtect`.
19//!     - On Unix: `mlock` and `mprotect`.
20//!
21//! 3.  **Memory Encryption (`std` on Windows only)**: On Windows, memory is also encrypted
22//!     in place using `CryptProtectMemory`, providing an additional layer of protection
23//!     against memory inspection.
24//!
25//! 4.  **Scoped Access**: Data is protected by default. To access it, you must use scoped
26//!     methods like `.unlocked_scope(|slice| { ... })`, which temporarily makes the data
27//!     accessible and automatically re-locks it afterward.
28//!
29//! ## Usage Example
30//!
31//! Here's a quick example of how to use `SecureString`:
32//!
33//! ```rust
34//! use secure_types::SecureString;
35//!
36//! // Create a string from a sensitive literal.
37//! // The original data is securely zeroized after being copied.
38//! let mut secret = SecureString::from("my_super_secret_password");
39//!
40//! // The memory is locked and protected here. Direct access is not possible.
41//!
42//! // Use a scope to safely access the content as a &str.
43//! secret.str_scope(|unlocked_str| {
44//!     assert_eq!(unlocked_str, "my_super_secret_password");
45//!     println!("The secret is: {}", unlocked_str);
46//! });
47//!
48//! // The memory is automatically locked again when the scope ends.
49//!
50//! // When `secret` goes out of scope, its memory will be securely zeroized.
51//! ```
52//!
53//! ## Feature Flags
54//!
55//! - `std` (default): Enables all OS-level security features like memory locking and encryption.
56//! - `serde`: Enables serialization and deserialization for `SecureString` and `SecureBytes` via the Serde framework.
57//! - `no_std`: Compiles the crate in a `no_std` environment. In this mode, only the **Zeroize on Drop**
58//!   guarantee is provided. This is useful for embedded systems or WebAssembly.
59
60#![cfg_attr(feature = "no_std", no_std)]
61
62#[cfg(feature = "no_std")]
63extern crate alloc;
64
65pub mod array;
66pub mod string;
67pub mod vec;
68
69pub use array::SecureArray;
70pub use string::SecureString;
71pub use vec::{SecureBytes, SecureVec};
72
73use core::ptr::NonNull;
74pub use zeroize::Zeroize;
75
76#[cfg(feature = "std")]
77pub use memsec;
78#[cfg(feature = "std")]
79use memsec::Prot;
80
81use thiserror::Error as ThisError;
82
83#[cfg(feature = "std")]
84#[derive(ThisError, Debug)]
85#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
86pub enum Error {
87   #[error("Failed to allocate secure memory")]
88   AllocationFailed,
89   #[error("Allocated Ptr is null")]
90   NullAllocation,
91   #[error("CryptProtectMemory failed")]
92   CryptProtectMemoryFailed,
93   #[error("CryptUnprotectMemory failed")]
94   CryptUnprotectMemoryFailed,
95   #[error("Failed to lock memory")]
96   LockFailed,
97   #[error("Failed to unlock memory")]
98   UnlockFailed,
99}
100
101#[cfg(not(feature = "std"))]
102#[derive(Debug)]
103pub enum Error {
104   AllocationFailed,
105   NullAllocation,
106}
107
108#[cfg(all(feature = "std", test, windows))]
109use windows_sys::Win32::Foundation::GetLastError;
110#[cfg(all(feature = "std", windows))]
111use windows_sys::Win32::Security::Cryptography::{
112   CRYPTPROTECTMEMORY_BLOCK_SIZE, CRYPTPROTECTMEMORY_SAME_PROCESS, CryptProtectMemory,
113   CryptUnprotectMemory,
114};
115#[cfg(all(feature = "std", windows))]
116use windows_sys::Win32::System::SystemInformation::GetSystemInfo;
117
118#[cfg(feature = "std")]
119pub fn page_size() -> usize {
120   #[cfg(unix)]
121   {
122      unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
123   }
124
125   #[cfg(windows)]
126   {
127      let mut si = core::mem::MaybeUninit::uninit();
128      unsafe {
129         GetSystemInfo(si.as_mut_ptr());
130         (*si.as_ptr()).dwPageSize as usize
131      }
132   }
133}
134
135#[cfg(feature = "std")]
136pub fn mprotect<T>(ptr: NonNull<T>, prot: Prot::Ty) -> bool {
137   let success = unsafe { memsec::mprotect(ptr, prot) };
138   if !success {
139      #[cfg(test)]
140      eprintln!("mprotect failed");
141   }
142   success
143}
144
145#[cfg(all(feature = "std", windows))]
146pub fn crypt_protect_memory(ptr: *mut u8, size_in_bytes: usize) -> bool {
147   if size_in_bytes == 0 {
148      return true; // Nothing to encrypt
149   }
150
151   if size_in_bytes % (CRYPTPROTECTMEMORY_BLOCK_SIZE as usize) != 0 {
152      // not a multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE
153      return false;
154   }
155
156   if size_in_bytes > u32::MAX as usize {
157      return false;
158   }
159
160   let result = unsafe {
161      CryptProtectMemory(
162         ptr as *mut core::ffi::c_void,
163         size_in_bytes as u32,
164         CRYPTPROTECTMEMORY_SAME_PROCESS,
165      )
166   };
167
168   if result == 0 {
169      #[cfg(test)]
170      {
171         let error_code = unsafe { GetLastError() };
172         eprintln!(
173            "CryptProtectMemory failed with error code: {}",
174            error_code
175         );
176      }
177      return false;
178   } else {
179      true
180   }
181}
182
183#[cfg(all(feature = "std", windows))]
184pub fn crypt_unprotect_memory(ptr: *mut u8, size_in_bytes: usize) -> bool {
185   if size_in_bytes == 0 {
186      return true;
187   }
188
189   if size_in_bytes % (CRYPTPROTECTMEMORY_BLOCK_SIZE as usize) != 0 {
190      return false;
191   }
192
193   if size_in_bytes > u32::MAX as usize {
194      return false;
195   }
196
197   let result = unsafe {
198      CryptUnprotectMemory(
199         ptr as *mut core::ffi::c_void,
200         size_in_bytes as u32,
201         CRYPTPROTECTMEMORY_SAME_PROCESS,
202      )
203   };
204
205   if result == 0 {
206      #[cfg(test)]
207      {
208         let error_code = unsafe { GetLastError() };
209         eprintln!(
210            "CryptUnprotectMemory failed with error code: {}",
211            error_code
212         );
213      }
214      return false;
215   } else {
216      true
217   }
218}