secure_string/secure_types/
vec.rs1use core::fmt;
2use std::{
3 borrow::{Borrow, BorrowMut},
4 str::FromStr,
5};
6
7use zeroize::Zeroize;
8
9use crate::secure_utils::memlock;
10
11#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
24pub struct SecureVec<T>
25where
26 T: Copy + Zeroize,
27{
28 pub(crate) content: Vec<T>,
29}
30
31pub type SecureBytes = SecureVec<u8>;
33
34impl<T> SecureVec<T>
35where
36 T: Copy + Zeroize,
37{
38 pub fn new(mut cont: Vec<T>) -> Self {
39 memlock::mlock(cont.as_mut_ptr(), cont.capacity());
40 SecureVec { content: cont }
41 }
42
43 pub fn unsecure(&self) -> &[T] {
45 self.borrow()
46 }
47
48 pub fn unsecure_mut(&mut self) -> &mut [T] {
50 self.borrow_mut()
51 }
52
53 pub fn resize(&mut self, new_len: usize, value: T) {
62 if new_len <= self.content.len() {
64 self.content.truncate(new_len);
65 return;
66 }
67
68 let mut new_vec = vec![value; new_len];
70 memlock::mlock(new_vec.as_mut_ptr(), new_vec.capacity());
71 new_vec[0..self.content.len()].copy_from_slice(&self.content);
72
73 self.zero_out();
75 memlock::munlock(self.content.as_mut_ptr(), self.content.capacity());
76 self.content = new_vec;
77 }
78
79 pub fn zero_out(&mut self) {
83 self.content.zeroize()
84 }
85}
86
87impl<T: Copy + Zeroize> Clone for SecureVec<T> {
88 fn clone(&self) -> Self {
89 Self::new(self.content.clone())
90 }
91}
92
93impl<T, U> From<U> for SecureVec<T>
95where
96 U: Into<Vec<T>>,
97 T: Copy + Zeroize,
98{
99 fn from(s: U) -> SecureVec<T> {
100 SecureVec::new(s.into())
101 }
102}
103
104impl FromStr for SecureVec<u8> {
105 type Err = std::convert::Infallible;
106
107 fn from_str(s: &str) -> Result<Self, Self::Err> {
108 Ok(SecureVec::new(s.into()))
109 }
110}
111
112impl<T, U> std::ops::Index<U> for SecureVec<T>
114where
115 T: Copy + Zeroize,
116 Vec<T>: std::ops::Index<U>,
117{
118 type Output = <Vec<T> as std::ops::Index<U>>::Output;
119
120 fn index(&self, index: U) -> &Self::Output {
121 std::ops::Index::index(&self.content, index)
122 }
123}
124
125impl<T> Borrow<[T]> for SecureVec<T>
127where
128 T: Copy + Zeroize,
129{
130 fn borrow(&self) -> &[T] {
131 self.content.borrow()
132 }
133}
134
135impl<T> BorrowMut<[T]> for SecureVec<T>
136where
137 T: Copy + Zeroize,
138{
139 fn borrow_mut(&mut self) -> &mut [T] {
140 self.content.borrow_mut()
141 }
142}
143
144impl<T> Drop for SecureVec<T>
146where
147 T: Copy + Zeroize,
148{
149 fn drop(&mut self) {
150 self.zero_out();
151 memlock::munlock(self.content.as_mut_ptr(), self.content.capacity());
152 }
153}
154
155impl<T> fmt::Debug for SecureVec<T>
157where
158 T: Copy + Zeroize,
159{
160 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
161 f.write_str("***SECRET***").map_err(|_| fmt::Error)
162 }
163}
164
165impl<T> fmt::Display for SecureVec<T>
166where
167 T: Copy + Zeroize,
168{
169 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170 f.write_str("***SECRET***").map_err(|_| fmt::Error)
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::{SecureBytes, SecureVec};
177
178 #[test]
179 fn test_basic() {
180 let my_sec = SecureBytes::from("hello");
181 assert_eq!(my_sec, SecureBytes::from("hello".to_string()));
182 assert_eq!(my_sec.unsecure(), b"hello");
183 }
184
185 #[test]
186 #[cfg_attr(feature = "pre", pre::pre)]
187 fn test_zero_out() {
188 let mut my_sec = SecureBytes::from("hello");
189 my_sec.zero_out();
190 #[cfg_attr(
192 feature = "pre",
193 forward(impl pre::std::vec::Vec),
194 assure(
195 new_len <= self.capacity(),
196 reason = "the call to `zero_out` did not reduce the capacity and the length was `5` before,
197 so the capacity must be greater or equal to `5`"
198 ),
199 assure(
200 "the elements at `old_len..new_len` are initialized",
201 reason = "they were initialized to `0` by the call to `zero_out`"
202 )
203 )]
204 unsafe {
205 my_sec.content.set_len(5)
206 }
207 assert_eq!(my_sec.unsecure(), b"\x00\x00\x00\x00\x00");
208 }
209
210 #[test]
211 fn test_resize() {
212 let mut my_sec = SecureVec::from([0, 1]);
213 assert_eq!(my_sec.unsecure().len(), 2);
214 my_sec.resize(1, 0);
215 assert_eq!(my_sec.unsecure().len(), 1);
216 my_sec.resize(16, 2);
217 assert_eq!(my_sec.unsecure(), &[0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]);
218 }
219
220 #[test]
221 fn test_comparison() {
222 assert_eq!(SecureBytes::from("hello"), SecureBytes::from("hello"));
223 assert!(SecureBytes::from("hello") != SecureBytes::from("yolo"));
224 assert!(SecureBytes::from("hello") != SecureBytes::from("olleh"));
225 assert!(SecureBytes::from("hello") != SecureBytes::from("helloworld"));
226 assert!(SecureBytes::from("hello") != SecureBytes::from(""));
227 }
228
229 #[test]
230 fn test_indexing() {
231 let string = SecureBytes::from("hello");
232 assert_eq!(string[0], b'h');
233 assert_eq!(&string[3..5], "lo".as_bytes());
234 }
235
236 #[test]
237 fn test_show() {
238 assert_eq!(format!("{:?}", SecureBytes::from("hello")), "***SECRET***".to_string());
239 assert_eq!(format!("{}", SecureBytes::from("hello")), "***SECRET***".to_string());
240 }
241
242 #[test]
243 #[cfg_attr(feature = "pre", pre::pre)]
244 fn test_comparison_zero_out_mb() {
245 let mbstring1 = SecureVec::from(vec!['H', 'a', 'l', 'l', 'o', ' ', '🦄', '!']);
246 let mbstring2 = SecureVec::from(vec!['H', 'a', 'l', 'l', 'o', ' ', '🦄', '!']);
247 let mbstring3 = SecureVec::from(vec!['!', '🦄', ' ', 'o', 'l', 'l', 'a', 'H']);
248 assert!(mbstring1 == mbstring2);
249 assert!(mbstring1 != mbstring3);
250
251 let mut mbstring = mbstring1.clone();
252 mbstring.zero_out();
253 #[cfg_attr(
255 feature = "pre",
256 forward(impl pre::std::vec::Vec),
257 assure(
258 new_len <= self.capacity(),
259 reason = "the call to `zero_out` did not reduce the capacity and the length was `8` before,
260 so the capacity must be greater or equal to `8`"
261 ),
262 assure(
263 "the elements at `old_len..new_len` are initialized",
264 reason = "they were initialized to `0` by the call to `zero_out`"
265 )
266 )]
267 unsafe {
268 mbstring.content.set_len(8)
269 }
270 assert_eq!(mbstring.unsecure(), &['\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0']);
271 }
272}