base64_ng/v2/secret/
owned.rs1use super::{ExposedSecret, ExposedSecretMut};
4use crate::v2::bounded::BufferLengthError;
5
6pub struct SecretArray<const CAP: usize> {
8 bytes: [u8; CAP],
9 len: usize,
10}
11
12impl<const CAP: usize> SecretArray<CAP> {
13 pub(super) fn from_frame(bytes: [u8; CAP], len: usize) -> Result<Self, BufferLengthError> {
14 Self::from_array(bytes, len)
15 }
16
17 pub fn from_array(mut bytes: [u8; CAP], len: usize) -> Result<Self, BufferLengthError> {
19 if len > CAP {
20 crate::wipe_bytes(&mut bytes);
21 return Err(BufferLengthError::new(len, CAP));
22 }
23 crate::wipe_tail(&mut bytes, len);
24 Ok(Self { bytes, len })
25 }
26
27 #[must_use]
29 pub fn expose_secret(&self) -> ExposedSecret<'_> {
30 ExposedSecret::new(&self.bytes[..self.len])
31 }
32
33 #[must_use]
35 pub fn expose_secret_mut(&mut self) -> ExposedSecretMut<'_> {
36 ExposedSecretMut {
37 bytes: &mut self.bytes[..self.len],
38 }
39 }
40
41 #[must_use = "declassification transfers cleanup responsibility to the caller"]
43 pub fn declassify(mut self) -> DeclassifiedArray<CAP> {
44 let bytes = core::mem::replace(&mut self.bytes, [0u8; CAP]);
45 let len = self.len;
46 self.len = 0;
47 DeclassifiedArray { bytes, len }
48 }
49
50 #[must_use]
52 pub const fn len(&self) -> usize {
53 self.len
54 }
55
56 #[must_use]
58 pub const fn is_empty(&self) -> bool {
59 self.len == 0
60 }
61
62 #[must_use]
64 pub const fn capacity(&self) -> usize {
65 CAP
66 }
67
68 pub fn clear(&mut self) {
70 crate::wipe_bytes(&mut self.bytes);
71 self.len = 0;
72 }
73
74 #[cfg(test)]
75 pub(crate) const fn backing_for_test(&self) -> &[u8; CAP] {
76 &self.bytes
77 }
78
79 #[cfg(kani)]
80 pub(crate) const fn backing_for_proof(&self) -> &[u8; CAP] {
81 &self.bytes
82 }
83}
84
85impl<const CAP: usize> Drop for SecretArray<CAP> {
86 fn drop(&mut self) {
87 self.clear();
88 }
89}
90
91impl<const CAP: usize> core::fmt::Debug for SecretArray<CAP> {
92 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
93 formatter
94 .debug_struct("SecretArray")
95 .field("bytes", &"<redacted>")
96 .field("len", &self.len)
97 .field("capacity", &CAP)
98 .finish()
99 }
100}
101
102impl<const CAP: usize> core::fmt::Display for SecretArray<CAP> {
103 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
104 formatter.write_str("<redacted secret array>")
105 }
106}
107
108#[derive(Clone, Copy, Eq, Hash, PartialEq)]
112pub struct DeclassifiedArray<const CAP: usize> {
113 bytes: [u8; CAP],
114 len: usize,
115}
116
117impl<const CAP: usize> DeclassifiedArray<CAP> {
118 #[must_use]
120 pub fn as_bytes(&self) -> &[u8] {
121 &self.bytes[..self.len]
122 }
123
124 #[must_use]
126 pub const fn len(&self) -> usize {
127 self.len
128 }
129
130 #[must_use]
132 pub const fn is_empty(&self) -> bool {
133 self.len == 0
134 }
135
136 #[must_use]
138 pub const fn capacity(&self) -> usize {
139 CAP
140 }
141
142 #[must_use]
144 pub const fn into_parts(self) -> ([u8; CAP], usize) {
145 (self.bytes, self.len)
146 }
147}
148
149impl<const CAP: usize> AsRef<[u8]> for DeclassifiedArray<CAP> {
150 fn as_ref(&self) -> &[u8] {
151 self.as_bytes()
152 }
153}
154
155impl<const CAP: usize> core::fmt::Debug for DeclassifiedArray<CAP> {
156 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
157 formatter
158 .debug_tuple("DeclassifiedArray")
159 .field(&self.as_bytes())
160 .finish()
161 }
162}
163
164#[cfg(feature = "alloc")]
166pub struct SecretVec {
167 bytes: alloc::vec::Vec<u8>,
168}
169
170#[cfg(feature = "alloc")]
171impl SecretVec {
172 pub(super) fn from_frame(mut bytes: alloc::vec::Vec<u8>, len: usize) -> Self {
173 debug_assert!(len <= bytes.len());
174 bytes.truncate(len);
175 crate::wipe_vec_spare_capacity(&mut bytes);
176 Self { bytes }
177 }
178
179 #[must_use]
181 pub fn from_vec(mut bytes: alloc::vec::Vec<u8>) -> Self {
182 crate::wipe_vec_spare_capacity(&mut bytes);
183 Self { bytes }
184 }
185
186 #[must_use]
188 pub fn from_slice(bytes: &[u8]) -> Self {
189 Self::from_vec(bytes.to_vec())
190 }
191
192 pub fn replace_from_vec(&mut self, replacement: alloc::vec::Vec<u8>) {
199 drop(self.replace_and_wipe_displaced(replacement));
200 }
201
202 fn replace_and_wipe_displaced(
203 &mut self,
204 mut replacement: alloc::vec::Vec<u8>,
205 ) -> alloc::vec::Vec<u8> {
206 crate::wipe_vec_spare_capacity(&mut replacement);
207 let mut displaced = core::mem::replace(&mut self.bytes, replacement);
208 crate::wipe_vec_all(&mut displaced);
209 displaced
210 }
211
212 #[cfg(test)]
213 pub(crate) fn replace_for_test(
214 &mut self,
215 replacement: alloc::vec::Vec<u8>,
216 ) -> alloc::vec::Vec<u8> {
217 self.replace_and_wipe_displaced(replacement)
218 }
219
220 #[must_use]
222 pub fn expose_secret(&self) -> ExposedSecret<'_> {
223 ExposedSecret::new(&self.bytes)
224 }
225
226 #[must_use]
228 pub fn expose_secret_mut(&mut self) -> ExposedSecretMut<'_> {
229 ExposedSecretMut {
230 bytes: &mut self.bytes,
231 }
232 }
233
234 #[must_use = "caller must apply its approved cleanup policy to the returned Vec"]
236 pub fn declassify_into_unprotected_vec(mut self) -> alloc::vec::Vec<u8> {
237 core::mem::take(&mut self.bytes)
238 }
239
240 #[must_use]
242 pub fn len(&self) -> usize {
243 self.bytes.len()
244 }
245
246 #[must_use]
248 pub fn is_empty(&self) -> bool {
249 self.bytes.is_empty()
250 }
251
252 #[must_use]
254 pub fn capacity(&self) -> usize {
255 self.bytes.capacity()
256 }
257
258 pub fn clear(&mut self) {
260 crate::wipe_vec_all(&mut self.bytes);
261 self.bytes.clear();
262 }
263}
264
265#[cfg(feature = "alloc")]
266impl Drop for SecretVec {
267 fn drop(&mut self) {
268 self.clear();
269 }
270}
271
272#[cfg(feature = "alloc")]
273redacted_formatting!(SecretVec, "SecretVec");