1use crate::RevealSecret;
19use crate::RevealSecretMut;
20
21#[cfg(feature = "encoding-base64")]
22use crate::traits::encoding::base64_url::ToBase64Url;
23#[cfg(feature = "encoding-hex")]
24use crate::traits::encoding::hex::ToHex;
25
26#[cfg(feature = "rand")]
27use rand::{rngs::SysRng, TryRng};
28use zeroize::Zeroize;
29
30#[cfg(feature = "encoding-base64")]
31use crate::traits::decoding::base64_url::FromBase64UrlStr;
32#[cfg(feature = "encoding-bech32")]
33use crate::traits::decoding::bech32::FromBech32Str;
34#[cfg(feature = "encoding-bech32m")]
35use crate::traits::decoding::bech32m::FromBech32mStr;
36#[cfg(feature = "encoding-hex")]
37use crate::traits::decoding::hex::FromHexStr;
38
39pub struct Fixed<T: zeroize::Zeroize> {
48 inner: T,
49}
50
51impl<T: zeroize::Zeroize> Fixed<T> {
52 #[inline(always)]
54 pub const fn new(value: T) -> Self {
55 Fixed { inner: value }
56 }
57}
58
59impl<const N: usize> From<[u8; N]> for Fixed<[u8; N]> {
60 #[inline(always)]
61 fn from(arr: [u8; N]) -> Self {
62 Self::new(arr)
63 }
64}
65
66impl<const N: usize> core::convert::TryFrom<&[u8]> for Fixed<[u8; N]> {
67 type Error = crate::error::FromSliceError;
68
69 fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
70 if slice.len() != N {
71 #[cfg(debug_assertions)]
72 return Err(crate::error::FromSliceError::InvalidLength {
73 actual: slice.len(),
74 expected: N,
75 });
76 #[cfg(not(debug_assertions))]
77 return Err(crate::error::FromSliceError::InvalidLength);
78 }
79 let mut arr = [0u8; N];
80 arr.copy_from_slice(slice);
81 Ok(Self::new(arr))
82 }
83}
84
85impl<const N: usize> Fixed<[u8; N]> {
87 #[cfg(feature = "encoding-hex")]
88 #[inline]
89 pub fn to_hex(&self) -> alloc::string::String {
90 self.with_secret(|s: &[u8; N]| s.to_hex())
91 }
92
93 #[cfg(feature = "encoding-hex")]
94 #[inline]
95 pub fn to_hex_upper(&self) -> alloc::string::String {
96 self.with_secret(|s: &[u8; N]| s.to_hex_upper())
97 }
98
99 #[cfg(feature = "encoding-base64")]
100 #[inline]
101 pub fn to_base64url(&self) -> alloc::string::String {
102 self.with_secret(|s: &[u8; N]| s.to_base64url())
103 }
104}
105
106impl<const N: usize, T: zeroize::Zeroize> RevealSecret for Fixed<[T; N]> {
108 type Inner = [T; N];
109
110 #[inline(always)]
111 fn with_secret<F, R>(&self, f: F) -> R
112 where
113 F: FnOnce(&[T; N]) -> R,
114 {
115 f(&self.inner)
116 }
117
118 #[inline(always)]
119 fn expose_secret(&self) -> &[T; N] {
120 &self.inner
121 }
122
123 #[inline(always)]
124 fn len(&self) -> usize {
125 N * core::mem::size_of::<T>()
126 }
127}
128
129impl<const N: usize, T: zeroize::Zeroize> RevealSecretMut for Fixed<[T; N]> {
131 #[inline(always)]
132 fn with_secret_mut<F, R>(&mut self, f: F) -> R
133 where
134 F: FnOnce(&mut [T; N]) -> R,
135 {
136 f(&mut self.inner)
137 }
138
139 #[inline(always)]
140 fn expose_secret_mut(&mut self) -> &mut [T; N] {
141 &mut self.inner
142 }
143}
144
145#[cfg(feature = "rand")]
146impl<const N: usize> Fixed<[u8; N]> {
147 #[inline]
148 pub fn from_random() -> Self {
149 let mut bytes = [0u8; N];
150 SysRng
151 .try_fill_bytes(&mut bytes)
152 .expect("SysRng failure is a program error");
153 Self::from(bytes)
154 }
155}
156
157#[cfg(feature = "encoding-hex")]
158impl<const N: usize> Fixed<[u8; N]> {
159 pub fn try_from_hex(hex: &str) -> Result<Self, crate::error::HexError> {
160 let bytes = zeroize::Zeroizing::new(hex.try_from_hex()?);
161 if bytes.len() != N {
162 #[cfg(debug_assertions)]
163 return Err(crate::error::HexError::InvalidLength {
164 expected: N,
165 got: bytes.len(),
166 });
167 #[cfg(not(debug_assertions))]
168 return Err(crate::error::HexError::InvalidLength);
169 }
170 let mut arr = [0u8; N];
171 arr.copy_from_slice(&bytes);
172 Ok(Self::new(arr))
173 }
174}
175
176#[cfg(feature = "encoding-base64")]
177impl<const N: usize> Fixed<[u8; N]> {
178 pub fn try_from_base64url(s: &str) -> Result<Self, crate::error::Base64Error> {
179 let bytes = zeroize::Zeroizing::new(s.try_from_base64url()?);
180 if bytes.len() != N {
181 #[cfg(debug_assertions)]
182 return Err(crate::error::Base64Error::InvalidLength {
183 expected: N,
184 got: bytes.len(),
185 });
186 #[cfg(not(debug_assertions))]
187 return Err(crate::error::Base64Error::InvalidLength);
188 }
189 let mut arr = [0u8; N];
190 arr.copy_from_slice(&bytes);
191 Ok(Self::new(arr))
192 }
193}
194
195#[cfg(feature = "encoding-bech32")]
196impl<const N: usize> Fixed<[u8; N]> {
197 pub fn try_from_bech32_unchecked(s: &str) -> Result<Self, crate::error::Bech32Error> {
205 let (_hrp, bytes_raw) = s.try_from_bech32_unchecked()?;
206 let bytes = zeroize::Zeroizing::new(bytes_raw);
207 if bytes.len() != N {
208 #[cfg(debug_assertions)]
209 return Err(crate::error::Bech32Error::InvalidLength {
210 expected: N,
211 got: bytes.len(),
212 });
213 #[cfg(not(debug_assertions))]
214 return Err(crate::error::Bech32Error::InvalidLength);
215 }
216 let mut arr = [0u8; N];
217 arr.copy_from_slice(&bytes);
218 Ok(Self::new(arr))
219 }
220
221 pub fn try_from_bech32(s: &str, expected_hrp: &str) -> Result<Self, crate::error::Bech32Error> {
227 let bytes_raw = s.try_from_bech32(expected_hrp)?;
228 let bytes = zeroize::Zeroizing::new(bytes_raw);
229 if bytes.len() != N {
230 #[cfg(debug_assertions)]
231 return Err(crate::error::Bech32Error::InvalidLength {
232 expected: N,
233 got: bytes.len(),
234 });
235 #[cfg(not(debug_assertions))]
236 return Err(crate::error::Bech32Error::InvalidLength);
237 }
238 let mut arr = [0u8; N];
239 arr.copy_from_slice(&bytes);
240 Ok(Self::new(arr))
241 }
242}
243
244#[cfg(feature = "encoding-bech32m")]
245impl<const N: usize> Fixed<[u8; N]> {
246 pub fn try_from_bech32m_unchecked(s: &str) -> Result<Self, crate::error::Bech32Error> {
254 let (_hrp, bytes_raw) = s.try_from_bech32m_unchecked()?;
255 let bytes = zeroize::Zeroizing::new(bytes_raw);
256 if bytes.len() != N {
257 #[cfg(debug_assertions)]
258 return Err(crate::error::Bech32Error::InvalidLength {
259 expected: N,
260 got: bytes.len(),
261 });
262 #[cfg(not(debug_assertions))]
263 return Err(crate::error::Bech32Error::InvalidLength);
264 }
265 let mut arr = [0u8; N];
266 arr.copy_from_slice(&bytes);
267 Ok(Self::new(arr))
268 }
269
270 pub fn try_from_bech32m(s: &str, expected_hrp: &str) -> Result<Self, crate::error::Bech32Error> {
276 let bytes_raw = s.try_from_bech32m(expected_hrp)?;
277 let bytes = zeroize::Zeroizing::new(bytes_raw);
278 if bytes.len() != N {
279 #[cfg(debug_assertions)]
280 return Err(crate::error::Bech32Error::InvalidLength {
281 expected: N,
282 got: bytes.len(),
283 });
284 #[cfg(not(debug_assertions))]
285 return Err(crate::error::Bech32Error::InvalidLength);
286 }
287 let mut arr = [0u8; N];
288 arr.copy_from_slice(&bytes);
289 Ok(Self::new(arr))
290 }
291}
292
293#[cfg(feature = "ct-eq")]
294impl<T: zeroize::Zeroize> crate::ConstantTimeEq for Fixed<T>
295where
296 T: crate::ConstantTimeEq,
297{
298 fn ct_eq(&self, other: &Self) -> bool {
299 self.inner.ct_eq(&other.inner)
300 }
301}
302
303impl<T: zeroize::Zeroize> core::fmt::Debug for Fixed<T> {
304 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
305 f.write_str("[REDACTED]")
306 }
307}
308
309#[cfg(feature = "cloneable")]
310impl<T: zeroize::Zeroize + crate::CloneableSecret> Clone for Fixed<T> {
311 fn clone(&self) -> Self {
312 Self::new(self.inner.clone())
313 }
314}
315
316#[cfg(feature = "serde-serialize")]
317impl<T: zeroize::Zeroize + crate::SerializableSecret> serde::Serialize for Fixed<T> {
318 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
319 where
320 S: serde::Serializer,
321 {
322 self.inner.serialize(serializer)
323 }
324}
325
326#[cfg(feature = "serde-deserialize")]
327impl<'de, const N: usize> serde::Deserialize<'de> for Fixed<[u8; N]> {
328 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
329 where
330 D: serde::Deserializer<'de>,
331 {
332 use core::fmt;
333 use serde::de::Visitor;
334 struct FixedVisitor<const M: usize>;
335 impl<'de, const M: usize> Visitor<'de> for FixedVisitor<M> {
336 type Value = Fixed<[u8; M]>;
337 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
338 write!(formatter, "a byte array of length {}", M)
339 }
340 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
341 where
342 A: serde::de::SeqAccess<'de>,
343 {
344 let mut vec: zeroize::Zeroizing<alloc::vec::Vec<u8>> =
345 zeroize::Zeroizing::new(alloc::vec::Vec::with_capacity(M));
346 while let Some(value) = seq.next_element()? {
347 vec.push(value);
348 }
349 if vec.len() != M {
350 #[cfg(debug_assertions)]
351 return Err(serde::de::Error::invalid_length(
352 vec.len(),
353 &M.to_string().as_str(),
354 ));
355 #[cfg(not(debug_assertions))]
356 return Err(serde::de::Error::custom("decoded length mismatch"));
357 }
358 let mut arr = [0u8; M];
359 arr.copy_from_slice(&vec);
360 Ok(Fixed::new(arr))
361 }
362 }
363 deserializer.deserialize_seq(FixedVisitor::<N>)
364 }
365}
366
367impl<T: zeroize::Zeroize> zeroize::Zeroize for Fixed<T> {
369 fn zeroize(&mut self) {
370 self.inner.zeroize();
371 }
372}
373
374impl<T: zeroize::Zeroize> Drop for Fixed<T> {
375 fn drop(&mut self) {
376 self.zeroize();
377 }
378}
379
380impl<T: zeroize::Zeroize> zeroize::ZeroizeOnDrop for Fixed<T> {}