1#[cfg(not(feature = "use_os"))]
5use alloc::alloc::Layout;
6#[cfg(not(feature = "use_os"))]
7use alloc::vec::Vec;
8
9use super::{Error, SecureVec, alloc};
10use core::{marker::PhantomData, mem, ptr::NonNull};
11use zeroize::Zeroize;
12
13#[cfg(feature = "use_os")]
14use super::free;
15#[cfg(feature = "use_os")]
16use memsec::Prot;
17
18struct UnlockGuard<'a, T: Zeroize, const LENGTH: usize> {
21 array: &'a SecureArray<T, LENGTH>,
22}
23
24impl<'a, T: Zeroize, const LENGTH: usize> UnlockGuard<'a, T, LENGTH> {
25 fn new(array: &'a SecureArray<T, LENGTH>) -> Self {
26 let ok = array.unlock_memory();
27 debug_assert!(ok, "UnlockGuard::new: unlock_memory failed");
28 UnlockGuard { array }
29 }
30}
31
32impl<'a, T: Zeroize, const LENGTH: usize> Drop for UnlockGuard<'a, T, LENGTH> {
33 fn drop(&mut self) {
34 let ok = self.array.lock_memory();
35 assert!(ok, "UnlockGuard::drop: lock_memory failed");
38 }
39}
40
41pub struct SecureArray<T, const LENGTH: usize>
93where
94 T: Zeroize,
95{
96 ptr: NonNull<T>,
97 initialized: usize,
103 _marker: PhantomData<T>,
104}
105
106unsafe impl<T: Zeroize + Send, const LENGTH: usize> Send for SecureArray<T, LENGTH> {}
107
108impl<T, const LENGTH: usize> SecureArray<T, LENGTH>
109where
110 T: Zeroize,
111{
112 pub fn empty() -> Result<Self, Error> {
122 let size = LENGTH
123 .checked_mul(mem::size_of::<T>())
124 .ok_or(Error::AllocationFailed)?;
125 if size == 0 {
126 return Err(Error::LengthCannotBeZero);
128 }
129
130 let ptr = unsafe { alloc::<T>(size)? };
134
135 let secure_array = SecureArray {
136 ptr,
137 initialized: 0,
138 _marker: PhantomData,
139 };
140
141 let _locked = secure_array.lock_memory();
142
143 #[cfg(feature = "use_os")]
144 if !_locked {
145 return Err(Error::LockFailed);
146 }
147
148 Ok(secure_array)
149 }
150
151 pub fn from_slice_mut(content: &mut [T; LENGTH]) -> Result<Self, Error>
155 where
156 T: Clone,
157 {
158 let mut secure_array = match Self::empty() {
159 Ok(secure_array) => secure_array,
160 Err(e) => {
161 content.zeroize();
162 return Err(e);
163 }
164 };
165
166 {
167 let _guard = UnlockGuard::new(&secure_array);
168
169 unsafe {
175 let dst = secure_array.ptr.as_ptr();
176 for (i, item) in content.iter().enumerate() {
177 core::ptr::write(dst.add(i), item.clone());
178 }
179 }
180 }
181 secure_array.initialized = LENGTH;
182
183 content.zeroize();
184
185 Ok(secure_array)
186 }
187
188 pub fn from_slice(content: &[T; LENGTH]) -> Result<Self, Error>
192 where
193 T: Clone,
194 {
195 let mut secure_array = Self::empty()?;
196
197 {
198 let _guard = UnlockGuard::new(&secure_array);
199
200 unsafe {
204 let dst = secure_array.ptr.as_ptr();
205 for (i, item) in content.iter().enumerate() {
206 core::ptr::write(dst.add(i), item.clone());
207 }
208 }
209 }
210 secure_array.initialized = LENGTH;
211
212 Ok(secure_array)
213 }
214
215 pub fn len(&self) -> usize {
216 LENGTH
217 }
218
219 pub fn is_empty(&self) -> bool {
220 self.len() == 0
221 }
222
223 #[cfg(feature = "expose-ptr")]
231 #[deprecated(
232 since = "0.3.0",
233 note = "This method is intended only for testing/crash reproduction. Use unlock() or unlock_mut() instead."
234 )]
235 pub fn ptr(&self) -> NonNull<T> {
236 self.ptr
237 }
238
239 pub(crate) fn lock_memory(&self) -> bool {
240 #[cfg(feature = "use_os")]
241 {
242 #[cfg(windows)]
243 {
244 super::mprotect(self.ptr, Prot::NoAccess)
245 }
246 #[cfg(unix)]
247 {
248 super::mprotect(self.ptr, Prot::NoAccess)
249 }
250 }
251 #[cfg(not(feature = "use_os"))]
252 {
253 true }
255 }
256
257 pub(crate) fn unlock_memory(&self) -> bool {
258 #[cfg(feature = "use_os")]
259 {
260 #[cfg(windows)]
261 {
262 super::mprotect(self.ptr, Prot::ReadWrite)
263 }
264 #[cfg(unix)]
265 {
266 super::mprotect(self.ptr, Prot::ReadWrite)
267 }
268 }
269
270 #[cfg(not(feature = "use_os"))]
271 {
272 true }
274 }
275
276 pub fn unlock<F, R>(&self, f: F) -> R
282 where
283 F: FnOnce(&[T]) -> R,
284 {
285 let _guard = UnlockGuard::new(self);
286 let slice = unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.initialized) };
289 f(slice)
290 }
291
292 pub fn unlock_mut<F, R>(&mut self, f: F) -> R
297 where
298 F: FnOnce(&mut [T]) -> R,
299 {
300 self.initialized = LENGTH;
301
302 let _guard = UnlockGuard::new(self);
303 let slice = unsafe { core::slice::from_raw_parts_mut(self.ptr.as_ptr(), LENGTH) };
308 f(slice)
309 }
310
311 pub fn erase(&mut self) {
313 let _guard = UnlockGuard::new(self);
314
315 unsafe {
319 let slice = core::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.initialized);
320 for element in slice.iter_mut() {
321 element.zeroize();
322 }
323 }
324 }
325
326 pub(crate) fn init_from_clone(&mut self, src: &[T])
329 where
330 T: Clone,
331 {
332 debug_assert_eq!(src.len(), LENGTH);
333
334 {
335 let _guard = UnlockGuard::new(self);
336
337 unsafe {
341 let dst = self.ptr.as_ptr();
342 for (i, item) in src.iter().enumerate() {
343 core::ptr::write(dst.add(i), item.clone());
344 }
345 }
346 }
347 self.initialized = src.len();
350 }
351}
352
353impl<T: Zeroize, const LENGTH: usize> Drop for SecureArray<T, LENGTH> {
354 fn drop(&mut self) {
355 let ok = self.unlock_memory();
356 debug_assert!(ok, "SecureArray::drop: unlock_memory failed");
357
358 let slice = unsafe { core::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.initialized) };
366 for element in slice.iter_mut() {
367 element.zeroize();
368 }
369
370 let size = LENGTH.checked_mul(mem::size_of::<T>()).unwrap_or(0);
371 if size == 0 {
372 return;
373 }
374
375 #[cfg(feature = "use_os")]
376 free(self.ptr);
377
378 #[cfg(not(feature = "use_os"))]
379 unsafe {
383 let bytes = core::slice::from_raw_parts_mut(self.ptr.as_ptr() as *mut u8, size);
384 bytes.zeroize();
385
386 let layout = Layout::from_size_align_unchecked(size, mem::align_of::<T>());
387 alloc::alloc::dealloc(self.ptr.as_ptr() as *mut u8, layout);
388 }
389 }
390}
391
392impl<T: Clone + Zeroize, const LENGTH: usize> Clone for SecureArray<T, LENGTH> {
393 fn clone(&self) -> Self {
397 let mut new_array = Self::empty().unwrap();
398 self.unlock(|src_slice| {
399 new_array.init_from_clone(src_slice);
400 });
401 new_array
402 }
403}
404
405impl<T: Clone + Zeroize, const LENGTH: usize> TryFrom<SecureVec<T>> for SecureArray<T, LENGTH> {
406 type Error = Error;
407
408 fn try_from(vec: SecureVec<T>) -> Result<Self, Self::Error> {
416 if vec.len() != LENGTH {
417 return Err(Error::LengthMismatch);
418 }
419
420 let mut new_array = Self::empty()?;
421
422 vec.unlock_slice(|vec_slice| {
423 new_array.init_from_clone(vec_slice);
424 });
425
426 Ok(new_array)
427 }
428}
429
430impl<T: Clone + Zeroize, const LENGTH: usize> TryFrom<Vec<T>> for SecureArray<T, LENGTH> {
431 type Error = Error;
432
433 fn try_from(mut vec: Vec<T>) -> Result<Self, Self::Error> {
441 if vec.len() != LENGTH {
442 vec.zeroize();
443 return Err(Error::LengthMismatch);
444 }
445
446 let mut new_array = match Self::empty() {
447 Ok(new_array) => new_array,
448 Err(e) => {
449 vec.zeroize();
450 return Err(e);
451 }
452 };
453
454 new_array.init_from_clone(&vec);
455 vec.zeroize();
456
457 Ok(new_array)
458 }
459}
460
461#[cfg(feature = "serde")]
466impl<const LENGTH: usize> serde::Serialize for SecureArray<u8, LENGTH> {
467 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
468 where
469 S: serde::Serializer,
470 {
471 self.unlock(|slice| serializer.serialize_bytes(slice))
472 }
473}
474
475#[cfg(feature = "serde")]
476impl<'de, const LENGTH: usize> serde::Deserialize<'de> for SecureArray<u8, LENGTH> {
477 fn deserialize<D>(deserializer: D) -> Result<SecureArray<u8, LENGTH>, D::Error>
478 where
479 D: serde::Deserializer<'de>,
480 {
481 struct SecureArrayVisitor<const L: usize>;
482
483 impl<'de, const L: usize> serde::de::Visitor<'de> for SecureArrayVisitor<L> {
484 type Value = SecureArray<u8, L>;
485
486 fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
487 write!(formatter, "a byte array of length {}", L)
488 }
489
490 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
491 where
492 A: serde::de::SeqAccess<'de>,
493 {
494 let mut data: SecureVec<u8> =
497 SecureVec::new_with_capacity(L).map_err(serde::de::Error::custom)?;
498
499 while let Some(byte) = seq.next_element::<u8>()? {
500 if data.len() == L {
501 return Err(serde::de::Error::invalid_length(
502 data.len() + 1,
503 &self,
504 ));
505 }
506
507 data.push(byte);
508 }
509
510 if data.len() != L {
512 return Err(serde::de::Error::invalid_length(
513 data.len(),
514 &self,
515 ));
516 }
517
518 SecureArray::try_from(data).map_err(serde::de::Error::custom)
519 }
520
521 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
524 where
525 E: serde::de::Error,
526 {
527 let bytes: &[u8; L] = v
528 .try_into()
529 .map_err(|_| serde::de::Error::invalid_length(v.len(), &self))?;
530
531 SecureArray::from_slice(bytes).map_err(serde::de::Error::custom)
532 }
533
534 fn visit_byte_buf<E>(self, mut v: Vec<u8>) -> Result<Self::Value, E>
537 where
538 E: serde::de::Error,
539 {
540 let array = self.visit_bytes(&v);
541 v.zeroize();
542 array
543 }
544 }
545
546 deserializer.deserialize_bytes(SecureArrayVisitor::<LENGTH>)
547 }
548}
549
550#[cfg(feature = "serde")]
556impl<const LENGTH: usize, T> serde::Serialize for SecureArray<T, LENGTH>
557where
558 T: crate::vec::SeqElement + serde::Serialize,
559{
560 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
561 where
562 S: serde::Serializer,
563 {
564 use serde::ser::SerializeTuple;
565
566 let mut tuple = serializer.serialize_tuple(LENGTH)?;
567
568 let elements: Result<(), S::Error> = self.unlock(|slice| {
569 for item in slice {
570 tuple.serialize_element(item)?;
571 }
572
573 Ok(())
574 });
575 elements?;
576
577 tuple.end()
578 }
579}
580
581#[cfg(feature = "serde")]
584impl<'de, const LENGTH: usize, T> serde::Deserialize<'de> for SecureArray<T, LENGTH>
585where
586 T: crate::vec::SeqElement + Clone + serde::Deserialize<'de>,
587{
588 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
589 where
590 D: serde::Deserializer<'de>,
591 {
592 struct SecureArraySeqVisitor<const L: usize, T>(::core::marker::PhantomData<T>);
593
594 impl<'de, const L: usize, T> serde::de::Visitor<'de> for SecureArraySeqVisitor<L, T>
595 where
596 T: crate::vec::SeqElement + Clone + serde::Deserialize<'de>,
597 {
598 type Value = SecureArray<T, L>;
599
600 fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
601 write!(formatter, "a secure array of length {}", L)
602 }
603
604 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
605 where
606 A: serde::de::SeqAccess<'de>,
607 {
608 let mut data: SecureVec<T> =
611 SecureVec::new_with_capacity(L).map_err(serde::de::Error::custom)?;
612
613 while let Some(element) = seq.next_element::<T>()? {
614 if data.len() == L {
615 return Err(serde::de::Error::invalid_length(
616 data.len() + 1,
617 &self,
618 ));
619 }
620
621 data.push(element);
622 }
623
624 if data.len() != L {
625 return Err(serde::de::Error::invalid_length(
626 data.len(),
627 &self,
628 ));
629 }
630
631 SecureArray::try_from(data).map_err(serde::de::Error::custom)
632 }
633 }
634
635 deserializer.deserialize_tuple(
636 LENGTH,
637 SecureArraySeqVisitor::<LENGTH, T>(::core::marker::PhantomData),
638 )
639 }
640}
641
642#[cfg(all(test, feature = "use_os"))]
643mod tests {
644 use super::*;
645 use std::process::{Command, Stdio};
646
647 #[test]
648 fn lock_unlock() {
649 let exposed: &mut [u8; 3] = &mut [1, 2, 3];
650 let secure: SecureArray<u8, 3> = SecureArray::from_slice_mut(exposed).unwrap();
651
652 let unlocked = secure.unlock_memory();
653 assert!(unlocked);
654
655 let locked = secure.lock_memory();
656 assert!(locked);
657 }
658
659 #[test]
661 fn test_initialized_count_tracking() {
662 let mut array: SecureArray<u8, 3> = SecureArray::empty().unwrap();
663 assert_eq!(array.initialized, 0);
664
665 array.unlock_mut(|slice| {
666 slice[0] = 1;
667 slice[1] = 2;
668 slice[2] = 3;
669 });
670 assert_eq!(array.initialized, 3);
671
672 let from_slice: SecureArray<u8, 3> = SecureArray::from_slice(&[1, 2, 3]).unwrap();
673 assert_eq!(from_slice.initialized, 3);
674 }
675
676 #[test]
677 fn test_index_should_fail_when_locked() {
678 let arg = "CRASH_TEST_ARRAY_LOCKED";
679
680 if std::env::args().any(|a| a == arg) {
681 let exposed: &mut [u8; 3] = &mut [1, 2, 3];
682 let array: SecureArray<u8, 3> = SecureArray::from_slice_mut(exposed).unwrap();
683 let _value = unsafe { core::hint::black_box(*array.ptr.as_ptr()) };
687
688 std::process::exit(1);
689 }
690
691 let child = Command::new(std::env::current_exe().unwrap())
692 .arg("array::tests::test_index_should_fail_when_locked")
693 .arg(arg)
694 .arg("--nocapture")
695 .stdout(Stdio::piped())
696 .stderr(Stdio::piped())
697 .spawn()
698 .expect("Failed to spawn child process");
699
700 let output = child.wait_with_output().expect("Failed to wait on child");
701 let status = output.status;
702
703 assert!(
704 !status.success(),
705 "Process exited successfully with code {:?}, but it should have crashed.",
706 status.code()
707 );
708
709 #[cfg(unix)]
710 {
711 use std::os::unix::process::ExitStatusExt;
712 let signal = status
713 .signal()
714 .expect("Process was not terminated by a signal on Unix.");
715 assert!(
716 signal == libc::SIGSEGV || signal == libc::SIGBUS,
717 "Process terminated with unexpected signal: {}",
718 signal
719 );
720 println!(
721 "Test passed: Process correctly terminated with signal {}.",
722 signal
723 );
724 }
725
726 #[cfg(windows)]
727 {
728 const STATUS_ACCESS_VIOLATION: i32 = 0xC0000005_u32 as i32;
729 assert_eq!(
730 status.code(),
731 Some(STATUS_ACCESS_VIOLATION),
732 "Process exited with unexpected code: {:x?}. Expected STATUS_ACCESS_VIOLATION.",
733 status.code()
734 );
735 eprintln!("Test passed: Process correctly terminated with STATUS_ACCESS_VIOLATION.");
736 }
737 }
738}