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
17pub struct SecretArrayVec<T: SecureSanitize, const CAP: usize> {
22 inner: ArrayVec<T, CAP>,
23}
24
25impl<T: SecureSanitize, const CAP: usize> SecretArrayVec<T, CAP> {
26 #[must_use]
28 #[inline]
29 pub const fn new() -> Self {
30 Self {
31 inner: ArrayVec::new_const(),
32 }
33 }
34
35 #[must_use]
37 #[inline]
38 pub const fn from_arrayvec(inner: ArrayVec<T, CAP>) -> Self {
39 Self { inner }
40 }
41
42 #[must_use]
44 #[inline]
45 pub fn len(&self) -> usize {
46 self.inner.len()
47 }
48
49 #[must_use]
51 #[inline]
52 pub const fn capacity(&self) -> usize {
53 CAP
54 }
55
56 #[must_use]
58 #[inline]
59 pub fn is_empty(&self) -> bool {
60 self.inner.is_empty()
61 }
62
63 #[inline]
65 pub fn push(&mut self, value: T) -> Result<(), CapacityError<T>> {
66 self.inner.try_push(value)
67 }
68
69 #[must_use]
71 #[inline]
72 pub fn as_slice(&self) -> &[T] {
73 self.inner.as_slice()
74 }
75
76 #[must_use]
78 #[inline]
79 pub fn as_mut_slice(&mut self) -> &mut [T] {
80 self.inner.as_mut_slice()
81 }
82
83 #[inline]
85 pub fn with_secret<R>(&self, inspect: impl FnOnce(&[T]) -> R) -> R {
86 inspect(self.as_slice())
87 }
88
89 #[inline]
91 pub fn with_secret_mut<R>(&mut self, edit: impl FnOnce(&mut [T]) -> R) -> R {
92 edit(self.as_mut_slice())
93 }
94
95 #[inline]
97 pub fn clear_secret(&mut self) {
98 self.inner.as_mut_slice().secure_sanitize();
99 self.inner.clear();
100 }
101
102 #[inline]
104 pub fn into_cleared(mut self) {
105 self.clear_secret();
106 }
107}
108
109impl<T: SecureSanitize, const CAP: usize> Default for SecretArrayVec<T, CAP> {
110 #[inline]
111 fn default() -> Self {
112 Self::new()
113 }
114}
115
116impl<T: SecureSanitize, const CAP: usize> SecureSanitize for SecretArrayVec<T, CAP> {
117 #[inline]
118 fn secure_sanitize(&mut self) {
119 self.clear_secret();
120 }
121}
122
123impl<T: SecureSanitize, const CAP: usize> Drop for SecretArrayVec<T, CAP> {
124 #[inline]
125 fn drop(&mut self) {
126 self.clear_secret();
127 }
128}
129
130impl<T: SecureSanitize, const CAP: usize> fmt::Debug for SecretArrayVec<T, CAP> {
131 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
132 formatter
133 .debug_struct("SecretArrayVec")
134 .field("len", &self.len())
135 .field("capacity", &CAP)
136 .field("contents", &"<redacted>")
137 .finish()
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 use sanitization::SecretBytes;
145
146 #[test]
147 fn arrayvec_wrapper_clears_live_elements() {
148 let mut secrets = SecretArrayVec::<SecretBytes<4>, 2>::new();
149
150 secrets.push(SecretBytes::from_array([1, 2, 3, 4])).unwrap();
151 secrets.push(SecretBytes::from_array([5, 6, 7, 8])).unwrap();
152
153 assert_eq!(secrets.len(), 2);
154 assert!(secrets.with_secret(|items| items[0].constant_time_eq(&[1, 2, 3, 4])));
155
156 secrets.clear_secret();
157
158 assert!(secrets.is_empty());
159 }
160
161 #[test]
162 fn arrayvec_wrapper_debug_is_redacted() {
163 let mut secrets = SecretArrayVec::<SecretBytes<4>, 2>::new();
164 secrets.push(SecretBytes::from_array([1, 2, 3, 4])).unwrap();
165
166 let rendered = std::format!("{secrets:?}");
167
168 assert!(rendered.contains("redacted"));
169 assert!(!rendered.contains("1, 2, 3, 4"));
170 }
171}