cortex_airlock/
secure_mem.rs1use zeroize::Zeroize;
2
3pub struct LockedBuffer {
9 data: Vec<u8>,
10 locked: bool,
11}
12
13impl LockedBuffer {
14 pub fn new(capacity: usize) -> Self {
16 let data = vec![0u8; capacity];
17 let locked = Self::mlock_region(data.as_ptr(), data.len());
18
19 if !locked {
20 tracing::warn!(
21 "Failed to mlock {} bytes — data may be swapped to disk",
22 capacity
23 );
24 }
25
26 Self { data, locked }
27 }
28
29 pub fn write(&mut self, src: &[u8]) -> usize {
31 let len = src.len().min(self.data.len());
32 self.data[..len].copy_from_slice(&src[..len]);
33 len
34 }
35
36 pub fn as_bytes(&self) -> &[u8] {
38 &self.data
39 }
40
41 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
43 &mut self.data
44 }
45
46 pub fn len(&self) -> usize {
47 self.data.len()
48 }
49
50 pub fn is_empty(&self) -> bool {
51 self.data.is_empty()
52 }
53
54 pub fn is_locked(&self) -> bool {
55 self.locked
56 }
57
58 pub fn wipe(&mut self) {
60 self.data.zeroize();
61 if self.locked {
62 Self::munlock_region(self.data.as_ptr(), self.data.len());
63 self.locked = false;
64 }
65 }
66
67 #[cfg(unix)]
68 fn mlock_region(ptr: *const u8, len: usize) -> bool {
69 unsafe { libc::mlock(ptr as *const libc::c_void, len) == 0 }
70 }
71
72 #[cfg(unix)]
73 fn munlock_region(ptr: *const u8, len: usize) {
74 unsafe {
75 libc::munlock(ptr as *const libc::c_void, len);
76 }
77 }
78
79 #[cfg(not(unix))]
80 fn mlock_region(_ptr: *const u8, _len: usize) -> bool {
81 false }
83
84 #[cfg(not(unix))]
85 fn munlock_region(_ptr: *const u8, _len: usize) {}
86}
87
88impl Drop for LockedBuffer {
89 fn drop(&mut self) {
90 self.wipe();
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn test_locked_buffer_write_read() {
100 let mut buf = LockedBuffer::new(32);
101 let data = b"secret content";
102 buf.write(data);
103 assert_eq!(&buf.as_bytes()[..data.len()], data);
104 }
105
106 #[test]
107 fn test_locked_buffer_wipe() {
108 let mut buf = LockedBuffer::new(16);
109 buf.write(b"secret");
110 buf.wipe();
111 assert!(buf.as_bytes().iter().all(|&b| b == 0));
112 }
113
114 #[test]
115 fn test_locked_buffer_drop_zeroizes() {
116 let ptr: *const u8;
117 let len: usize;
118 {
119 let mut buf = LockedBuffer::new(8);
120 buf.write(b"12345678");
121 ptr = buf.as_bytes().as_ptr();
122 len = buf.len();
123 }
125 let _ = (ptr, len);
128 }
129}