1use zeroize::Zeroize as _;
2
3const LEN: usize = 4096;
4
5static MLOCK_WORKS: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
6
7struct MlockGuard {
12 ptr: *mut core::ffi::c_void,
13 len: usize,
14}
15
16unsafe impl Send for MlockGuard {}
19unsafe impl Sync for MlockGuard {}
20
21impl Drop for MlockGuard {
22 fn drop(&mut self) {
23 let _ = unsafe { rustix::mm::munlock(self.ptr, self.len) };
27 }
28}
29
30fn try_mlock(ptr: *const u8, len: usize) -> rustix::io::Result<MlockGuard> {
31 let p = ptr.cast::<core::ffi::c_void>().cast_mut();
34 unsafe { rustix::mm::mlock(p, len) }?;
37 Ok(MlockGuard { ptr: p, len })
38}
39
40pub struct FixedVec<const N: usize> {
41 data: [u8; N],
42 len: usize,
43}
44
45impl<const N: usize> FixedVec<N> {
46 fn new() -> Self {
47 Self {
48 data: [0u8; N],
49 len: 0,
50 }
51 }
52
53 const fn capacity() -> usize {
54 N
55 }
56
57 fn as_ptr(&self) -> *const u8 {
58 self.data.as_ptr()
59 }
60
61 fn as_slice(&self) -> &[u8] {
62 &self.data[..self.len]
63 }
64
65 fn as_mut_slice(&mut self) -> &mut [u8] {
66 &mut self.data[..self.len]
67 }
68
69 fn truncate(&mut self, len: usize) {
70 if len < self.len {
71 self.len = len;
72 }
73 }
74
75 fn extend(&mut self, it: impl Iterator<Item = u8>) {
76 for b in it {
77 assert!(self.len < N, "FixedVec capacity exceeded");
78 self.data[self.len] = b;
79 self.len += 1;
80 }
81 }
82}
83
84impl<const N: usize> Drop for FixedVec<N> {
85 fn drop(&mut self) {
86 self.data[..self.len].zeroize();
87 }
88}
89
90pub struct Vec {
91 data: Box<FixedVec<LEN>>,
92 _lock: Option<MlockGuard>,
93}
94
95impl Default for Vec {
96 fn default() -> Self {
97 let data = Box::new(FixedVec::<LEN>::new());
98 let lock = match MLOCK_WORKS.get() {
99 Some(true) => {
100 try_mlock(data.as_ptr(), FixedVec::<LEN>::capacity()).ok()
101 }
102 Some(false) => None,
103 None => {
104 match try_mlock(data.as_ptr(), FixedVec::<LEN>::capacity()) {
105 Ok(lock) => {
106 let _ = MLOCK_WORKS.set(true);
107 Some(lock)
108 }
109 Err(e) => {
110 if MLOCK_WORKS.set(false).is_ok() {
111 eprintln!("failed to lock memory region: {e}");
112 }
113 None
114 }
115 }
116 }
117 };
118 Self { data, _lock: lock }
119 }
120}
121
122impl Vec {
123 pub fn new() -> Self {
124 Self::default()
125 }
126
127 pub fn data(&self) -> &[u8] {
128 self.data.as_slice()
129 }
130
131 pub fn data_mut(&mut self) -> &mut [u8] {
132 self.data.as_mut_slice()
133 }
134
135 pub fn zero(&mut self) {
136 self.truncate(0);
137 self.data.extend(std::iter::repeat_n(0, LEN));
138 }
139
140 pub fn extend(&mut self, it: impl Iterator<Item = u8>) {
141 self.data.extend(it);
142 }
143
144 pub fn truncate(&mut self, len: usize) {
145 self.data.truncate(len);
146 }
147}
148
149impl Drop for Vec {
150 fn drop(&mut self) {
151 self.zero();
152 self.data.as_mut_slice().zeroize();
153 }
154}
155
156impl Clone for Vec {
157 fn clone(&self) -> Self {
158 let mut new_vec = Self::new();
159 new_vec.extend(self.data().iter().copied());
160 new_vec
161 }
162}
163
164#[derive(Clone)]
165pub struct Password {
166 password: Vec,
167}
168
169impl Password {
170 pub fn new(password: Vec) -> Self {
171 Self { password }
172 }
173
174 pub fn password(&self) -> &[u8] {
175 self.password.data()
176 }
177}
178
179#[derive(Clone)]
180pub struct Keys {
181 keys: Vec,
182}
183
184impl Keys {
185 pub fn new(keys: Vec) -> Self {
186 Self { keys }
187 }
188
189 pub fn enc_key(&self) -> &[u8] {
190 &self.keys.data()[0..32]
191 }
192
193 pub fn mac_key(&self) -> &[u8] {
194 &self.keys.data()[32..64]
195 }
196
197 pub fn as_bytes(&self) -> &[u8] {
200 &self.keys.data()[0..64]
201 }
202}
203
204#[derive(Clone)]
205pub struct PasswordHash {
206 hash: Vec,
207}
208
209impl PasswordHash {
210 pub fn new(hash: Vec) -> Self {
211 Self { hash }
212 }
213
214 pub fn hash(&self) -> &[u8] {
215 self.hash.data()
216 }
217}
218
219#[derive(Clone)]
220pub struct PrivateKey {
221 private_key: Vec,
222}
223
224impl PrivateKey {
225 pub fn new(private_key: Vec) -> Self {
226 Self { private_key }
227 }
228
229 pub fn private_key(&self) -> &[u8] {
230 self.private_key.data()
231 }
232}
233
234#[derive(Clone)]
235pub struct ApiKey {
236 client_id: Password,
237 client_secret: Password,
238}
239
240impl ApiKey {
241 pub fn new(client_id: Password, client_secret: Password) -> Self {
242 Self {
243 client_id,
244 client_secret,
245 }
246 }
247
248 pub fn client_id(&self) -> &[u8] {
249 self.client_id.password()
250 }
251
252 pub fn client_secret(&self) -> &[u8] {
253 self.client_secret.password()
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use super::FixedVec;
260
261 #[test]
262 fn push_len_and_slice() {
263 let mut v = FixedVec::<8>::new();
264 v.extend([1u8, 2, 3, 4].into_iter());
265 assert_eq!(v.as_slice().len(), 4);
266 assert_eq!(v.as_slice(), &[1, 2, 3, 4]);
267 }
268
269 #[test]
270 fn truncate_and_clear() {
271 let mut v = FixedVec::<8>::new();
272 v.extend([1u8, 2, 3, 4].into_iter());
273 v.truncate(0);
274 assert!(v.as_slice().is_empty());
275 assert_eq!(v.data[..4], [1, 2, 3, 4]);
276 }
277
278 #[test]
279 #[should_panic(expected = "FixedVec capacity exceeded")]
280 fn push_past_capacity_panics() {
281 let mut v = FixedVec::<2>::new();
282 v.extend([1u8, 2, 3].into_iter());
283 }
284
285 #[test]
286 fn fixed_vec_drop_zeros_written_bytes() {
287 let mut v = FixedVec::<8>::new();
297 v.extend([0xaa_u8, 0xbb, 0xcc, 0xdd].into_iter());
298 assert_eq!(v.data[..4], [0xaa, 0xbb, 0xcc, 0xdd]);
299 {
301 use zeroize::Zeroize as _;
302 v.data[..v.len].zeroize();
303 }
304 assert_eq!(v.data[..4], [0, 0, 0, 0]);
305 }
306
307 #[test]
308 fn locked_vec_extend_and_data() {
309 let mut v = super::Vec::new();
310 v.extend([1_u8, 2, 3, 4].iter().copied());
311 assert_eq!(v.data(), &[1, 2, 3, 4]);
312 }
313
314 #[test]
315 fn locked_vec_zero_fills_and_exposes_full_slice() {
316 let mut v = super::Vec::new();
317 v.extend([9_u8; 16].iter().copied());
318 v.zero();
319 assert_eq!(v.data().len(), super::LEN);
323 assert!(v.data().iter().all(|b| *b == 0));
324 }
325
326 #[test]
327 fn locked_vec_truncate_shrinks_visible_slice() {
328 let mut v = super::Vec::new();
329 v.extend((0_u8..32).chain(std::iter::repeat_n(0, 0)));
330 assert_eq!(v.data().len(), 32);
331 v.truncate(8);
332 assert_eq!(v.data(), &(0_u8..8).collect::<std::vec::Vec<_>>()[..]);
333 }
334
335 #[test]
336 fn locked_vec_clone_is_independent() {
337 let mut original = super::Vec::new();
338 original.extend([1_u8, 2, 3, 4].iter().copied());
339 let copy = original.clone();
340 assert_eq!(copy.data(), &[1, 2, 3, 4]);
341 original.data_mut()[0] = 99;
343 assert_eq!(copy.data(), &[1, 2, 3, 4]);
344 }
345
346 #[test]
347 fn keys_exposes_enc_mac_split() {
348 let mut buf = super::Vec::new();
349 buf.extend((0_u8..64).collect::<std::vec::Vec<_>>().into_iter());
350 let k = super::Keys::new(buf);
351 assert_eq!(k.enc_key().len(), 32);
352 assert_eq!(k.mac_key().len(), 32);
353 assert_eq!(k.as_bytes().len(), 64);
354 assert_eq!(k.enc_key()[0], 0);
356 assert_eq!(k.enc_key()[31], 31);
357 assert_eq!(k.mac_key()[0], 32);
358 assert_eq!(k.mac_key()[31], 63);
359 }
360}