secure_types/
lib.rs

1#![doc = include_str!("../readme.md")]
2
3#![cfg_attr(feature = "no_std", no_std)]
4
5#[cfg(feature = "no_std")]
6extern crate alloc;
7
8pub mod array;
9pub mod string;
10pub mod vec;
11
12pub use array::SecureArray;
13pub use string::SecureString;
14pub use vec::{SecureBytes, SecureVec};
15
16use core::ptr::NonNull;
17pub use zeroize::Zeroize;
18
19#[cfg(feature = "std")]
20pub use memsec;
21#[cfg(feature = "std")]
22use memsec::Prot;
23
24use thiserror::Error as ThisError;
25
26#[cfg(feature = "std")]
27#[derive(ThisError, Debug)]
28#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
29pub enum Error {
30   #[error("Failed to allocate secure memory")]
31   AllocationFailed,
32   #[error("Length cannot be zero")]
33   LengthCannotBeZero,
34   #[error("Allocated Ptr is null")]
35   NullAllocation,
36   #[error("CryptProtectMemory failed")]
37   CryptProtectMemoryFailed,
38   #[error("CryptUnprotectMemory failed")]
39   CryptUnprotectMemoryFailed,
40   #[error("Failed to lock memory")]
41   LockFailed,
42   #[error("Failed to unlock memory")]
43   UnlockFailed,
44   #[error("Source length does not match the fixed size of the destination array")]
45   LengthMismatch,
46}
47
48#[cfg(not(feature = "std"))]
49#[derive(Debug)]
50pub enum Error {
51   AllocationFailed,
52   NullAllocation,
53}
54
55#[cfg(all(feature = "std", test, windows))]
56use windows_sys::Win32::Foundation::GetLastError;
57#[cfg(all(feature = "std", windows))]
58use windows_sys::Win32::Security::Cryptography::{
59   CRYPTPROTECTMEMORY_BLOCK_SIZE, CRYPTPROTECTMEMORY_SAME_PROCESS, CryptProtectMemory,
60   CryptUnprotectMemory,
61};
62#[cfg(all(feature = "std", windows))]
63use windows_sys::Win32::System::SystemInformation::GetSystemInfo;
64
65#[cfg(feature = "std")]
66/// Returns the page size depending on the OS
67pub fn page_size() -> usize {
68   #[cfg(unix)]
69   {
70      unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
71   }
72
73   #[cfg(windows)]
74   {
75      let mut si = core::mem::MaybeUninit::uninit();
76      unsafe {
77         GetSystemInfo(si.as_mut_ptr());
78         (*si.as_ptr()).dwPageSize as usize
79      }
80   }
81}
82
83#[cfg(feature = "std")]
84/// Returns the page aligned size of a given size
85pub fn page_aligned_size(size: usize) -> usize {
86   (size + page_size() - 1) & !(page_size() - 1)
87}
88
89#[cfg(feature = "std")]
90pub fn mprotect<T>(ptr: NonNull<T>, prot: Prot::Ty) -> bool {
91   let success = unsafe { memsec::mprotect(ptr, prot) };
92   if !success {
93      #[cfg(test)]
94      eprintln!("mprotect failed");
95   }
96   success
97}
98
99#[cfg(all(feature = "std", windows))]
100pub fn crypt_protect_memory(ptr: *mut u8, aligned_size: usize) -> bool {
101   if aligned_size == 0 {
102      return true; // Nothing to encrypt
103   }
104
105   if aligned_size % (CRYPTPROTECTMEMORY_BLOCK_SIZE as usize) != 0 {
106      // not a multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE
107      return false;
108   }
109
110   if aligned_size > u32::MAX as usize {
111      return false;
112   }
113
114   let result = unsafe {
115      CryptProtectMemory(
116         ptr as *mut core::ffi::c_void,
117         aligned_size as u32,
118         CRYPTPROTECTMEMORY_SAME_PROCESS,
119      )
120   };
121
122   if result == 0 {
123      #[cfg(test)]
124      {
125         let error_code = unsafe { GetLastError() };
126         eprintln!(
127            "CryptProtectMemory failed with error code: {}",
128            error_code
129         );
130      }
131      false
132   } else {
133      true
134   }
135}
136
137#[cfg(all(feature = "std", windows))]
138pub fn crypt_unprotect_memory(ptr: *mut u8, size_in_bytes: usize) -> bool {
139   if size_in_bytes == 0 {
140      return true;
141   }
142
143   if size_in_bytes % (CRYPTPROTECTMEMORY_BLOCK_SIZE as usize) != 0 {
144      return false;
145   }
146
147   if size_in_bytes > u32::MAX as usize {
148      return false;
149   }
150
151   let result = unsafe {
152      CryptUnprotectMemory(
153         ptr as *mut core::ffi::c_void,
154         size_in_bytes as u32,
155         CRYPTPROTECTMEMORY_SAME_PROCESS,
156      )
157   };
158
159   if result == 0 {
160      #[cfg(test)]
161      {
162         let error_code = unsafe { GetLastError() };
163         eprintln!(
164            "CryptUnprotectMemory failed with error code: {}",
165            error_code
166         );
167      }
168      false
169   } else {
170      true
171   }
172}
173
174#[cfg(test)]
175mod tests {
176   #[cfg(feature = "serde")]
177   #[test]
178   fn test_array_and_secure_vec_serde_compatibility() {
179      use super::*;
180      let exposed_array: &mut [u8; 3] = &mut [1, 2, 3];
181      let array: SecureArray<u8, 3> = SecureArray::from_slice_mut(exposed_array).unwrap();
182      let vec: SecureVec<u8> = array.clone().into();
183
184      let array_json_string = serde_json::to_string(&array).unwrap();
185      let array_json_bytes = serde_json::to_vec(&array).unwrap();
186      let vec_json_string = serde_json::to_string(&vec).unwrap();
187      let vec_json_bytes = serde_json::to_vec(&vec).unwrap();
188
189      assert_eq!(array_json_string, vec_json_string);
190      assert_eq!(array_json_bytes, vec_json_bytes);
191
192      let deserialized_array_from_string: SecureArray<u8, 3> =
193         serde_json::from_str(&array_json_string).unwrap();
194
195      let deserialized_array_from_bytes: SecureArray<u8, 3> =
196         serde_json::from_slice(&array_json_bytes).unwrap();
197
198      let deserialized_vec_from_string: SecureVec<u8> =
199         serde_json::from_str(&vec_json_string).unwrap();
200
201      let deserialized_vec_from_bytes: SecureVec<u8> =
202         serde_json::from_slice(&vec_json_bytes).unwrap();
203
204      deserialized_array_from_string.unlock(|slice| {
205         deserialized_vec_from_string.unlock_slice(|slice2| {
206            assert_eq!(slice, slice2);
207         });
208      });
209
210      deserialized_array_from_bytes.unlock(|slice| {
211         deserialized_vec_from_bytes.unlock_slice(|slice2| {
212            assert_eq!(slice, slice2);
213         });
214      });
215   }
216}