1#[cfg(not(feature = "use_os"))]
2use alloc::{Layout, alloc, dealloc};
3
4use super::{Error, SecureVec};
5use core::{marker::PhantomData, mem, ptr::NonNull};
6use zeroize::Zeroize;
7
8#[cfg(feature = "use_os")]
9use super::{alloc, free};
10#[cfg(feature = "use_os")]
11use memsec::Prot;
12
13pub struct SecureArray<T, const LENGTH: usize>
57where
58 T: Zeroize,
59{
60 ptr: NonNull<T>,
61 _marker: PhantomData<T>,
62}
63
64unsafe impl<T: Zeroize + Send, const LENGTH: usize> Send for SecureArray<T, LENGTH> {}
65unsafe impl<T: Zeroize + Send + Sync, const LENGTH: usize> Sync for SecureArray<T, LENGTH> {}
66
67impl<T, const LENGTH: usize> SecureArray<T, LENGTH>
68where
69 T: Zeroize,
70{
71 pub fn empty() -> Result<Self, Error> {
75 let size = LENGTH * mem::size_of::<T>();
76 if size == 0 {
77 return Err(Error::LengthCannotBeZero);
79 }
80
81 let ptr = unsafe { alloc::<T>(size)? };
82
83 let secure_array = SecureArray {
84 ptr,
85 _marker: PhantomData,
86 };
87
88 let locked = secure_array.lock_memory();
89
90 #[cfg(feature = "use_os")]
91 if !locked {
92 return Err(Error::LockFailed);
93 }
94
95 Ok(secure_array)
96 }
97
98 pub fn from_slice_mut(content: &mut [T; LENGTH]) -> Result<Self, Error> {
102 let secure_array = Self::empty()?;
103
104 let unlocked = secure_array.unlock_memory();
105
106 if !unlocked {
107 return Err(Error::UnlockFailed);
108 }
109
110 unsafe {
111 core::ptr::copy_nonoverlapping(
113 content.as_ptr(),
114 secure_array.ptr.as_ptr(),
115 LENGTH,
116 );
117 }
118
119 content.zeroize();
120
121 let locked = secure_array.lock_memory();
122
123 #[cfg(feature = "use_os")]
124 if !locked {
125 return Err(Error::LockFailed);
126 }
127
128 Ok(secure_array)
129 }
130
131 pub fn from_slice(content: &[T; LENGTH]) -> Result<Self, Error> {
135 let secure_array = Self::empty()?;
136
137 let unlocked = secure_array.unlock_memory();
138
139 if !unlocked {
140 return Err(Error::UnlockFailed);
141 }
142
143 unsafe {
144 core::ptr::copy_nonoverlapping(
146 content.as_ptr(),
147 secure_array.ptr.as_ptr(),
148 LENGTH,
149 );
150 }
151
152 let locked = secure_array.lock_memory();
153
154 #[cfg(feature = "use_os")]
155 if !locked {
156 return Err(Error::LockFailed);
157 }
158
159 Ok(secure_array)
160 }
161
162 pub fn len(&self) -> usize {
163 LENGTH
164 }
165
166 pub fn is_empty(&self) -> bool {
167 self.len() == 0
168 }
169
170 pub(crate) fn lock_memory(&self) -> bool {
171 #[cfg(feature = "use_os")]
172 {
173 #[cfg(windows)]
174 {
175 super::mprotect(self.ptr, Prot::NoAccess)
176 }
177 #[cfg(unix)]
178 {
179 super::mprotect(self.ptr, Prot::NoAccess)
180 }
181 }
182 #[cfg(not(feature = "use_os"))]
183 {
184 true }
186 }
187
188 pub(crate) fn unlock_memory(&self) -> bool {
189 #[cfg(feature = "use_os")]
190 {
191 #[cfg(windows)]
192 {
193 super::mprotect(self.ptr, Prot::ReadWrite)
194 }
195 #[cfg(unix)]
196 {
197 super::mprotect(self.ptr, Prot::ReadWrite)
198 }
199 }
200
201 #[cfg(not(feature = "use_os"))]
202 {
203 true }
205 }
206
207 pub fn unlock<F, R>(&self, f: F) -> R
209 where
210 F: FnOnce(&[T]) -> R,
211 {
212 self.unlock_memory();
213 let slice = unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), LENGTH) };
214 let result = f(slice);
215 self.lock_memory();
216 result
217 }
218
219 pub fn unlock_mut<F, R>(&mut self, f: F) -> R
221 where
222 F: FnOnce(&mut [T]) -> R,
223 {
224 self.unlock_memory();
225 let slice = unsafe { core::slice::from_raw_parts_mut(self.ptr.as_ptr(), LENGTH) };
226 let result = f(slice);
227 self.lock_memory();
228 result
229 }
230
231 pub fn erase(&mut self) {
233 self.unlock_mut(|slice| {
234 for element in slice.iter_mut() {
235 element.zeroize();
236 }
237 });
238 }
239
240 pub(crate) fn init_from_clone(&mut self, src: &[T])
243 where
244 T: Clone,
245 {
246 debug_assert_eq!(src.len(), LENGTH);
247
248 self.unlock_memory();
249 unsafe {
250 let dst = self.ptr.as_ptr();
251 for (i, item) in src.iter().enumerate() {
252 core::ptr::write(dst.add(i), item.clone());
253 }
254 }
255 self.lock_memory();
256 }
257}
258
259impl<T: Zeroize, const LENGTH: usize> core::ops::Index<usize> for SecureArray<T, LENGTH> {
260 type Output = T;
261 fn index(&self, index: usize) -> &Self::Output {
262 assert!(index < self.len(), "Index out of bounds");
263 unsafe {
264 let ptr = self.ptr.as_ptr().add(index);
265 &*ptr
266 }
267 }
268}
269
270impl<T: Zeroize, const LENGTH: usize> core::ops::IndexMut<usize> for SecureArray<T, LENGTH> {
271 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
272 assert!(index < self.len(), "Index out of bounds");
273 unsafe {
274 let ptr = self.ptr.as_ptr().add(index);
275 &mut *ptr
276 }
277 }
278}
279
280impl<T: Zeroize, const LENGTH: usize> Drop for SecureArray<T, LENGTH> {
281 fn drop(&mut self) {
282 self.erase();
283 self.unlock_memory();
284
285 let size = LENGTH * mem::size_of::<T>();
286 if size == 0 {
287 return;
288 }
289
290 #[cfg(feature = "use_os")]
291 free(self.ptr);
292
293 #[cfg(not(feature = "use_os"))]
294 {
295 let layout = Layout::from_size_align_unchecked(size, mem::align_of::<T>());
296 dealloc(self.ptr.as_ptr() as *mut u8, layout);
297 }
298 }
299}
300
301impl<T: Clone + Zeroize, const LENGTH: usize> Clone for SecureArray<T, LENGTH> {
302 fn clone(&self) -> Self {
303 let mut new_array = Self::empty().unwrap();
304 self.unlock(|src_slice| {
305 new_array.init_from_clone(src_slice);
306 });
307 new_array
308 }
309}
310
311impl<const LENGTH: usize> TryFrom<SecureVec<u8>> for SecureArray<u8, LENGTH> {
312 type Error = Error;
313
314 fn try_from(vec: SecureVec<u8>) -> Result<Self, Self::Error> {
320 if vec.len() != LENGTH {
321 return Err(Error::LengthMismatch);
322 }
323
324 let mut new_array = Self::empty()?;
325
326 vec.unlock_slice(|vec_slice| {
327 new_array.init_from_clone(vec_slice);
328 });
329
330 Ok(new_array)
331 }
332}
333
334#[cfg(feature = "serde")]
335impl<const LENGTH: usize> serde::Serialize for SecureArray<u8, LENGTH> {
336 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
337 where
338 S: serde::Serializer,
339 {
340 self.unlock(|slice| serializer.collect_seq(slice.iter()))
341 }
342}
343
344#[cfg(feature = "serde")]
345impl<'de, const LENGTH: usize> serde::Deserialize<'de> for SecureArray<u8, LENGTH> {
346 fn deserialize<D>(deserializer: D) -> Result<SecureArray<u8, LENGTH>, D::Error>
347 where
348 D: serde::Deserializer<'de>,
349 {
350 struct SecureArrayVisitor<const L: usize>;
351
352 impl<'de, const L: usize> serde::de::Visitor<'de> for SecureArrayVisitor<L> {
353 type Value = SecureArray<u8, L>;
354
355 fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
356 write!(formatter, "a byte array of length {}", L)
357 }
358
359 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
360 where
361 A: serde::de::SeqAccess<'de>,
362 {
363 let mut data: SecureVec<u8> =
364 SecureVec::new_with_capacity(L).map_err(serde::de::Error::custom)?;
365 while let Some(byte) = seq.next_element()? {
366 data.push(byte);
367 }
368
369 if data.len() != L {
371 return Err(serde::de::Error::invalid_length(
372 data.len(),
373 &self,
374 ));
375 }
376
377 SecureArray::try_from(data).map_err(serde::de::Error::custom)
378 }
379 }
380
381 deserializer.deserialize_bytes(SecureArrayVisitor::<LENGTH>)
382 }
383}
384
385#[cfg(all(test, feature = "use_os"))]
386mod tests {
387 use super::*;
388 use std::process::{Command, Stdio};
389 use std::sync::{Arc, Mutex};
390
391 #[test]
392 fn test_creation() {
393 let exposed_mut = &mut [1, 2, 3];
394 let array: SecureArray<u8, 3> = SecureArray::from_slice_mut(exposed_mut).unwrap();
395 assert_eq!(array.len(), 3);
396
397 array.unlock(|slice| {
398 assert_eq!(slice, &[1, 2, 3]);
399 });
400
401 assert_eq!(exposed_mut, &[0u8; 3]);
402
403 let exposed = &[1, 2, 3];
404 let array: SecureArray<u8, 3> = SecureArray::from_slice(exposed).unwrap();
405 assert_eq!(array.len(), 3);
406
407 array.unlock(|slice| {
408 assert_eq!(slice, &[1, 2, 3]);
409 });
410
411 assert_eq!(exposed, &[1, 2, 3]);
412 }
413
414 #[test]
415 fn test_from_secure_vec() {
416 let vec: SecureVec<u8> = SecureVec::from_slice(&[1, 2, 3]).unwrap();
417 let array: SecureArray<u8, 3> = vec.try_into().unwrap();
418 assert_eq!(array.len(), 3);
419 array.unlock(|slice| {
420 assert_eq!(slice, &[1, 2, 3]);
421 });
422 }
423
424 #[test]
425 fn test_erase() {
426 let exposed: &mut [u8; 3] = &mut [1, 2, 3];
427 let mut array: SecureArray<u8, 3> = SecureArray::from_slice_mut(exposed).unwrap();
428 array.erase();
429 array.unlock(|slice| {
430 assert_eq!(slice, &[0u8; 3]);
431 });
432 }
433
434 #[test]
435 #[should_panic]
436 fn test_length_cannot_be_zero() {
437 let secure_vec = SecureVec::new().unwrap();
438 let _secure_array: SecureArray<u8, 0> = SecureArray::try_from(secure_vec).unwrap();
439 }
440
441 #[test]
442 fn lock_unlock() {
443 let exposed: &mut [u8; 3] = &mut [1, 2, 3];
444 let secure: SecureArray<u8, 3> = SecureArray::from_slice_mut(exposed).unwrap();
445
446 let unlocked = secure.unlock_memory();
447 assert!(unlocked);
448
449 let locked = secure.lock_memory();
450 assert!(locked);
451 }
452
453 #[test]
454 fn test_clone() {
455 let mut array1: SecureArray<u8, 3> = SecureArray::empty().unwrap();
456 array1.unlock_mut(|slice| {
457 slice[0] = 1;
458 slice[1] = 2;
459 slice[2] = 3;
460 });
461
462 let array2 = array1.clone();
463
464 array2.unlock(|slice| {
465 assert_eq!(slice, &[1, 2, 3]);
466 });
467
468 array1.unlock(|slice| {
469 assert_eq!(slice, &[1, 2, 3]);
470 });
471 }
472
473 #[test]
474 fn test_thread_safety() {
475 let exposed: &mut [u8; 3] = &mut [1, 2, 3];
476 let array: SecureArray<u8, 3> = SecureArray::from_slice_mut(exposed).unwrap();
477 let arc_array = Arc::new(Mutex::new(array));
478 let mut handles = Vec::new();
479
480 for _ in 0..5u8 {
481 let array_clone = Arc::clone(&arc_array);
482 let handle = std::thread::spawn(move || {
483 let mut guard = array_clone.lock().unwrap();
484 guard.unlock_mut(|slice| {
485 slice[0] += 1;
486 });
487 });
488 handles.push(handle);
489 }
490
491 for handle in handles {
492 handle.join().unwrap();
493 }
494
495 let final_array = arc_array.lock().unwrap();
496 final_array.unlock(|slice| {
497 assert_eq!(slice[0], 6);
498 assert_eq!(slice[1], 2);
499 assert_eq!(slice[2], 3);
500 });
501 }
502
503 #[test]
504 fn test_index_should_fail_when_locked() {
505 let arg = "CRASH_TEST_ARRAY_LOCKED";
506
507 if std::env::args().any(|a| a == arg) {
508 let exposed: &mut [u8; 3] = &mut [1, 2, 3];
509 let array: SecureArray<u8, 3> = SecureArray::from_slice_mut(exposed).unwrap();
510 let _value = core::hint::black_box(array[0]);
511
512 std::process::exit(1);
513 }
514
515 let child = Command::new(std::env::current_exe().unwrap())
516 .arg("array::tests::test_index_should_fail_when_locked")
517 .arg(arg)
518 .arg("--nocapture")
519 .stdout(Stdio::piped())
520 .stderr(Stdio::piped())
521 .spawn()
522 .expect("Failed to spawn child process");
523
524 let output = child.wait_with_output().expect("Failed to wait on child");
525 let status = output.status;
526
527 assert!(
528 !status.success(),
529 "Process exited successfully with code {:?}, but it should have crashed.",
530 status.code()
531 );
532
533 #[cfg(unix)]
534 {
535 use std::os::unix::process::ExitStatusExt;
536 let signal = status
537 .signal()
538 .expect("Process was not terminated by a signal on Unix.");
539 assert!(
540 signal == libc::SIGSEGV || signal == libc::SIGBUS,
541 "Process terminated with unexpected signal: {}",
542 signal
543 );
544 println!(
545 "Test passed: Process correctly terminated with signal {}.",
546 signal
547 );
548 }
549
550 #[cfg(windows)]
551 {
552 const STATUS_ACCESS_VIOLATION: i32 = 0xC0000005_u32 as i32;
553 assert_eq!(
554 status.code(),
555 Some(STATUS_ACCESS_VIOLATION),
556 "Process exited with unexpected code: {:x?}. Expected STATUS_ACCESS_VIOLATION.",
557 status.code()
558 );
559 eprintln!("Test passed: Process correctly terminated with STATUS_ACCESS_VIOLATION.");
560 }
561 }
562
563 #[test]
564 fn test_unlock_mut() {
565 let exposed: &mut [u8; 3] = &mut [1, 2, 3];
566 let mut array: SecureArray<u8, 3> = SecureArray::from_slice_mut(exposed).unwrap();
567
568 array.unlock_mut(|slice| {
569 slice[1] = 100;
570 });
571
572 array.unlock(|slice| {
573 assert_eq!(slice, &[1, 100, 3]);
574 });
575 }
576
577 #[cfg(feature = "serde")]
578 #[test]
579 fn test_serde() {
580 let exposed: &mut [u8; 3] = &mut [1, 2, 3];
581 let array: SecureArray<u8, 3> = SecureArray::from_slice_mut(exposed).unwrap();
582 let json_string = serde_json::to_string(&array).expect("Serialization failed");
583 let json_bytes = serde_json::to_vec(&array).expect("Serialization failed");
584
585 let deserialized_string: SecureArray<u8, 3> =
586 serde_json::from_str(&json_string).expect("Deserialization failed");
587
588 let deserialized_bytes: SecureArray<u8, 3> =
589 serde_json::from_slice(&json_bytes).expect("Deserialization failed");
590
591 deserialized_string.unlock(|slice| {
592 assert_eq!(slice, &[1, 2, 3]);
593 });
594
595 deserialized_bytes.unlock(|slice| {
596 assert_eq!(slice, &[1, 2, 3]);
597 });
598 }
599}