Skip to main content

sanitization_arrayvec/
lib.rs

1#![no_std]
2#![deny(unsafe_code)]
3
4//! `arrayvec` integration wrappers for `sanitization`.
5//!
6//! This crate deliberately uses wrapper types instead of trait impls for
7//! external types. Rust's orphan rules prevent implementing
8//! `sanitization::SecureSanitize` directly for `arrayvec::ArrayVec` here.
9
10use arrayvec::{ArrayVec, CapacityError};
11use core::fmt;
12use sanitization::SecureSanitize;
13
14#[cfg(test)]
15extern crate std;
16
17/// Clear-on-drop wrapper around [`ArrayVec`].
18///
19/// Live elements are sanitized before the vector is cleared. Spare uninitialized
20/// storage is not treated as secret material because it has never held a `T`.
21pub struct SecretArrayVec<T: SecureSanitize, const CAP: usize> {
22    inner: ArrayVec<T, CAP>,
23}
24
25impl<T: SecureSanitize, const CAP: usize> SecretArrayVec<T, CAP> {
26    /// Create an empty secret array vector.
27    #[must_use]
28    #[inline]
29    pub const fn new() -> Self {
30        Self {
31            inner: ArrayVec::new_const(),
32        }
33    }
34
35    /// Wrap an existing [`ArrayVec`].
36    #[must_use]
37    #[inline]
38    pub const fn from_arrayvec(inner: ArrayVec<T, CAP>) -> Self {
39        Self { inner }
40    }
41
42    /// Number of initialized elements.
43    #[must_use]
44    #[inline]
45    pub fn len(&self) -> usize {
46        self.inner.len()
47    }
48
49    /// Maximum number of elements.
50    #[must_use]
51    #[inline]
52    pub const fn capacity(&self) -> usize {
53        CAP
54    }
55
56    /// Returns true when there are no initialized elements.
57    #[must_use]
58    #[inline]
59    pub fn is_empty(&self) -> bool {
60        self.inner.is_empty()
61    }
62
63    /// Push one sanitizable element.
64    #[inline]
65    pub fn push(&mut self, value: T) -> Result<(), CapacityError<T>> {
66        self.inner.try_push(value)
67    }
68
69    /// Borrow initialized elements.
70    #[must_use]
71    #[inline]
72    pub fn as_slice(&self) -> &[T] {
73        self.inner.as_slice()
74    }
75
76    /// Mutably borrow initialized elements.
77    #[must_use]
78    #[inline]
79    pub fn as_mut_slice(&mut self) -> &mut [T] {
80        self.inner.as_mut_slice()
81    }
82
83    /// Run a closure with read-only access to initialized elements.
84    #[inline]
85    pub fn with_secret<R>(&self, inspect: impl FnOnce(&[T]) -> R) -> R {
86        inspect(self.as_slice())
87    }
88
89    /// Run a closure with mutable access to initialized elements.
90    #[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    /// Sanitize all live elements and clear the vector.
96    #[inline]
97    pub fn clear_secret(&mut self) {
98        self.inner.as_mut_slice().secure_sanitize();
99        self.inner.clear();
100    }
101
102    /// Consume after first sanitizing all live elements.
103    #[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}