sanitization_arrayvec/
lib.rs1#![no_std]
2#![deny(unsafe_code)]
3
4use arrayvec::{ArrayVec, CapacityError};
11use core::fmt;
12use sanitization::SecureSanitize;
13
14#[cfg(test)]
15extern crate std;
16
17#[derive(Clone, Copy, Debug, Eq, PartialEq)]
20pub struct SanitizedCapacityError;
21
22impl fmt::Display for SanitizedCapacityError {
23 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
24 formatter.write_str("secret array vector is full; rejected element was sanitized")
25 }
26}
27
28impl core::error::Error for SanitizedCapacityError {}
29
30pub struct SecretArrayVec<T: SecureSanitize, const CAP: usize> {
35 inner: ArrayVec<T, CAP>,
36}
37
38impl<T: SecureSanitize, const CAP: usize> SecretArrayVec<T, CAP> {
39 #[must_use]
41 #[inline]
42 pub const fn new() -> Self {
43 Self {
44 inner: ArrayVec::new_const(),
45 }
46 }
47
48 #[must_use]
50 #[inline]
51 pub const fn from_arrayvec(inner: ArrayVec<T, CAP>) -> Self {
52 Self { inner }
53 }
54
55 #[must_use]
57 #[inline]
58 pub fn len(&self) -> usize {
59 self.inner.len()
60 }
61
62 #[must_use]
64 #[inline]
65 pub const fn capacity(&self) -> usize {
66 CAP
67 }
68
69 #[must_use]
71 #[inline]
72 pub fn is_empty(&self) -> bool {
73 self.inner.is_empty()
74 }
75
76 #[inline]
84 pub fn push(&mut self, value: T) -> Result<(), CapacityError<T>> {
85 self.inner.try_push(value)
86 }
87
88 #[inline]
93 pub fn push_or_sanitize(&mut self, value: T) -> Result<(), SanitizedCapacityError> {
94 match self.inner.try_push(value) {
95 Ok(()) => Ok(()),
96 Err(error) => {
97 let mut rejected = error.element();
98 rejected.secure_sanitize();
99 Err(SanitizedCapacityError)
100 }
101 }
102 }
103
104 #[must_use]
106 #[inline]
107 pub fn as_slice(&self) -> &[T] {
108 self.inner.as_slice()
109 }
110
111 #[must_use]
113 #[inline]
114 pub fn as_mut_slice(&mut self) -> &mut [T] {
115 self.inner.as_mut_slice()
116 }
117
118 #[inline]
120 pub fn with_secret<R>(&self, inspect: impl FnOnce(&[T]) -> R) -> R {
121 inspect(self.as_slice())
122 }
123
124 #[inline]
126 pub fn with_secret_mut<R>(&mut self, edit: impl FnOnce(&mut [T]) -> R) -> R {
127 edit(self.as_mut_slice())
128 }
129
130 #[inline]
132 pub fn clear_secret(&mut self) {
133 self.inner.as_mut_slice().secure_sanitize();
134 self.inner.clear();
135 }
136
137 #[inline]
139 pub fn into_cleared(mut self) {
140 self.clear_secret();
141 }
142}
143
144impl<T: SecureSanitize, const CAP: usize> Default for SecretArrayVec<T, CAP> {
145 #[inline]
146 fn default() -> Self {
147 Self::new()
148 }
149}
150
151impl<T: SecureSanitize, const CAP: usize> SecureSanitize for SecretArrayVec<T, CAP> {
152 #[inline]
153 fn secure_sanitize(&mut self) {
154 self.clear_secret();
155 }
156}
157
158impl<T: SecureSanitize, const CAP: usize> Drop for SecretArrayVec<T, CAP> {
159 #[inline]
160 fn drop(&mut self) {
161 self.clear_secret();
162 }
163}
164
165impl<T: SecureSanitize, const CAP: usize> fmt::Debug for SecretArrayVec<T, CAP> {
166 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
167 formatter
168 .debug_struct("SecretArrayVec")
169 .field("len", &self.len())
170 .field("capacity", &CAP)
171 .field("contents", &"<redacted>")
172 .finish()
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179 use sanitization::SecretBytes;
180
181 #[test]
182 fn arrayvec_wrapper_clears_live_elements() {
183 let mut secrets = SecretArrayVec::<SecretBytes<4>, 2>::new();
184
185 secrets.push(SecretBytes::from_array([1, 2, 3, 4])).unwrap();
186 secrets.push(SecretBytes::from_array([5, 6, 7, 8])).unwrap();
187
188 assert_eq!(secrets.len(), 2);
189 assert!(secrets.with_secret(|items| items[0].constant_time_eq(&[1, 2, 3, 4])));
190
191 secrets.clear_secret();
192
193 assert!(secrets.is_empty());
194 }
195
196 #[test]
197 fn arrayvec_wrapper_debug_is_redacted() {
198 let mut secrets = SecretArrayVec::<SecretBytes<4>, 2>::new();
199 secrets.push(SecretBytes::from_array([1, 2, 3, 4])).unwrap();
200
201 let rendered = std::format!("{secrets:?}");
202
203 assert!(rendered.contains("redacted"));
204 assert!(!rendered.contains("1, 2, 3, 4"));
205 }
206
207 #[test]
208 fn arrayvec_wrapper_push_returns_original_rejected_element() {
209 let mut secrets = SecretArrayVec::<[u8; 4], 0>::new();
210
211 let error = secrets.push([1, 2, 3, 4]).unwrap_err();
212
213 assert_eq!(error.element(), [1, 2, 3, 4]);
214 }
215
216 #[test]
217 fn arrayvec_wrapper_can_sanitize_rejected_elements() {
218 use core::sync::atomic::{AtomicBool, Ordering};
219 use std::sync::Arc;
220
221 struct Probe(Arc<AtomicBool>);
222
223 impl SecureSanitize for Probe {
224 fn secure_sanitize(&mut self) {
225 self.0.store(true, Ordering::Release);
226 }
227 }
228
229 let sanitized = Arc::new(AtomicBool::new(false));
230 let mut secrets = SecretArrayVec::<Probe, 0>::new();
231
232 assert_eq!(
233 secrets.push_or_sanitize(Probe(Arc::clone(&sanitized))),
234 Err(SanitizedCapacityError)
235 );
236 assert!(sanitized.load(Ordering::Acquire));
237 }
238}