securer_string/secure_types/
vec.rs1use core::fmt;
2use std::borrow::{Borrow, BorrowMut};
3use std::str::FromStr;
4
5use subtle::ConstantTimeEq;
6use zeroize::Zeroize;
7
8use crate::secure_utils::memlock;
9
10pub struct SecureVec<T>
30where
31 T: Copy + Zeroize,
32{
33 pub(crate) content: Vec<T>,
34 pub(crate) is_locked: bool,
37}
38
39pub type SecureBytes = SecureVec<u8>;
41
42impl<T> SecureVec<T>
43where
44 T: Copy + Zeroize,
45{
46 #[must_use]
47 pub fn new(mut cont: Vec<T>) -> Self {
48 let is_locked = memlock::mlock(cont.as_mut_ptr(), cont.capacity()).is_ok();
49 SecureVec {
50 content: cont,
51 is_locked,
52 }
53 }
54
55 #[must_use]
57 pub fn unsecure(&self) -> &[T] {
58 self.borrow()
59 }
60
61 #[must_use]
63 pub fn unsecure_mut(&mut self) -> &mut [T] {
64 self.borrow_mut()
65 }
66
67 pub fn resize(&mut self, new_len: usize, value: T) {
78 if new_len <= self.content.len() {
80 self.content.truncate(new_len);
81 return;
82 }
83
84 let mut new_vec = vec![value; new_len];
86 let new_is_locked = memlock::mlock(new_vec.as_mut_ptr(), new_vec.capacity()).is_ok();
87 new_vec[0..self.content.len()].copy_from_slice(&self.content);
88
89 self.zero_out();
91 if self.is_locked {
92 memlock::munlock(self.content.as_mut_ptr(), self.content.capacity());
93 }
94 self.content = new_vec;
95 self.is_locked = new_is_locked;
96 }
97
98 pub fn zero_out(&mut self) {
103 self.content.zeroize();
104 }
105}
106
107impl<T: Copy + Zeroize> Clone for SecureVec<T> {
108 fn clone(&self) -> Self {
109 Self::new(self.content.clone())
110 }
111}
112
113impl<T: Copy + Zeroize + ConstantTimeEq> ConstantTimeEq for SecureVec<T> {
114 fn ct_eq(&self, other: &Self) -> subtle::Choice {
115 self.content.ct_eq(&other.content)
116 }
117}
118
119impl<T: Copy + Zeroize + ConstantTimeEq> PartialEq for SecureVec<T> {
120 fn eq(&self, other: &Self) -> bool {
121 self.ct_eq(other).into()
122 }
123}
124
125impl<T: Copy + Zeroize + ConstantTimeEq> Eq for SecureVec<T> {}
126
127impl<T, U> From<U> for SecureVec<T>
129where
130 U: Into<Vec<T>>,
131 T: Copy + Zeroize,
132{
133 fn from(s: U) -> SecureVec<T> {
134 SecureVec::new(s.into())
135 }
136}
137
138impl FromStr for SecureVec<u8> {
139 type Err = std::convert::Infallible;
140
141 fn from_str(s: &str) -> Result<Self, Self::Err> {
142 Ok(SecureVec::new(s.into()))
143 }
144}
145
146impl<T, U> std::ops::Index<U> for SecureVec<T>
148where
149 T: Copy + Zeroize,
150 Vec<T>: std::ops::Index<U>,
151{
152 type Output = <Vec<T> as std::ops::Index<U>>::Output;
153
154 fn index(&self, index: U) -> &Self::Output {
155 std::ops::Index::index(&self.content, index)
156 }
157}
158
159impl<T> Borrow<[T]> for SecureVec<T>
161where
162 T: Copy + Zeroize,
163{
164 fn borrow(&self) -> &[T] {
165 self.content.borrow()
166 }
167}
168
169impl<T> BorrowMut<[T]> for SecureVec<T>
170where
171 T: Copy + Zeroize,
172{
173 fn borrow_mut(&mut self) -> &mut [T] {
174 self.content.borrow_mut()
175 }
176}
177
178impl<T> Drop for SecureVec<T>
180where
181 T: Copy + Zeroize,
182{
183 fn drop(&mut self) {
184 self.zero_out();
185 if self.is_locked {
186 memlock::munlock(self.content.as_mut_ptr(), self.content.capacity());
187 }
188 }
189}
190
191impl<T> fmt::Debug for SecureVec<T>
193where
194 T: Copy + Zeroize,
195{
196 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
197 f.debug_struct("SecureVec").finish_non_exhaustive()
198 }
199}
200
201impl<T> fmt::Display for SecureVec<T>
202where
203 T: Copy + Zeroize,
204{
205 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
206 f.write_str("***SECRET***").map_err(|_| fmt::Error)
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::{SecureBytes, SecureVec};
213
214 #[test]
215 fn test_basic() {
216 let my_sec = SecureBytes::from("hello");
217 assert_eq!(my_sec, SecureBytes::from("hello".to_string()));
218 assert_eq!(my_sec.unsecure(), b"hello");
219 }
220
221 #[test]
222 fn test_zero_out() {
223 let mut my_sec = SecureBytes::from("hello");
224 my_sec.zero_out();
225 unsafe {
228 my_sec.content.set_len(5);
229 }
230 assert_eq!(my_sec.unsecure(), b"\x00\x00\x00\x00\x00");
231 }
232
233 #[test]
234 fn test_resize() {
235 let mut my_sec = SecureVec::from([0, 1]);
236 assert_eq!(my_sec.unsecure().len(), 2);
237 my_sec.resize(1, 0);
238 assert_eq!(my_sec.unsecure().len(), 1);
239 my_sec.resize(16, 2);
240 assert_eq!(
241 my_sec.unsecure(),
242 &[0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
243 );
244 }
245
246 #[test]
247 fn test_comparison() {
248 assert_eq!(SecureBytes::from("hello"), SecureBytes::from("hello"));
249 assert_ne!(SecureBytes::from("hello"), SecureBytes::from("yolo"));
250 assert_ne!(SecureBytes::from("hello"), SecureBytes::from("olleh"));
251 assert_ne!(SecureBytes::from("hello"), SecureBytes::from("helloworld"));
252 assert_ne!(SecureBytes::from("hello"), SecureBytes::from(""));
253 }
254
255 #[test]
256 fn test_indexing() {
257 let string = SecureBytes::from("hello");
258 assert_eq!(string[0], b'h');
259 assert_eq!(&string[3..5], "lo".as_bytes());
260 }
261
262 #[test]
263 fn test_show() {
264 assert_eq!(
265 format!("{:?}", SecureBytes::from("hello")),
266 "SecureVec { .. }".to_string()
267 );
268 assert_eq!(
269 format!("{}", SecureBytes::from("hello")),
270 "***SECRET***".to_string()
271 );
272 }
273
274 #[test]
275 fn test_comparison_zero_out_mb() {
276 let mbstring1 = SecureVec::from(vec![
277 'H' as u32,
278 'a' as u32,
279 'l' as u32,
280 'l' as u32,
281 'o' as u32,
282 ' ' as u32,
283 '🦄' as u32,
284 '!' as u32,
285 ]);
286 let mbstring2 = SecureVec::from(vec![
287 'H' as u32,
288 'a' as u32,
289 'l' as u32,
290 'l' as u32,
291 'o' as u32,
292 ' ' as u32,
293 '🦄' as u32,
294 '!' as u32,
295 ]);
296 let mbstring3 = SecureVec::from(vec![
297 '!' as u32,
298 '🦄' as u32,
299 ' ' as u32,
300 'o' as u32,
301 'l' as u32,
302 'l' as u32,
303 'a' as u32,
304 'H' as u32,
305 ]);
306 assert_eq!(mbstring1, mbstring2);
307 assert_ne!(mbstring1, mbstring3);
308
309 let mut mbstring = mbstring1.clone();
310 mbstring.zero_out();
311 unsafe {
314 mbstring.content.set_len(8);
315 }
316 assert_eq!(mbstring.unsecure(), &[0u32; 8]);
317 }
318}