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")]
66pub 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")]
84pub 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; }
104
105 if aligned_size % (CRYPTPROTECTMEMORY_BLOCK_SIZE as usize) != 0 {
106 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}