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")]
92 #[inline]
93 pub fn to_hex(&self) -> alloc::string::String {
94 self.with_secret(|s: &[u8; N]| s.to_hex())
95 }
96
97 #[cfg(feature = "encoding-hex")]
102 #[inline]
103 pub fn to_hex_upper(&self) -> alloc::string::String {
104 self.with_secret(|s: &[u8; N]| s.to_hex_upper())
105 }
106
107 #[cfg(feature = "encoding-base64")]
112 #[inline]
113 pub fn to_base64url(&self) -> alloc::string::String {
114 self.with_secret(|s: &[u8; N]| s.to_base64url())
115 }
116}
117
118impl<const N: usize, T: zeroize::Zeroize> RevealSecret for Fixed<[T; N]> {
120 type Inner = [T; N];
121
122 #[inline(always)]
123 fn with_secret<F, R>(&self, f: F) -> R
124 where
125 F: FnOnce(&[T; N]) -> R,
126 {
127 f(&self.inner)
128 }
129
130 #[inline(always)]
131 fn expose_secret(&self) -> &[T; N] {
132 &self.inner
133 }
134
135 #[inline(always)]
136 fn len(&self) -> usize {
137 N * core::mem::size_of::<T>()
138 }
139}
140
141impl<const N: usize, T: zeroize::Zeroize> RevealSecretMut for Fixed<[T; N]> {
143 #[inline(always)]
144 fn with_secret_mut<F, R>(&mut self, f: F) -> R
145 where
146 F: FnOnce(&mut [T; N]) -> R,
147 {
148 f(&mut self.inner)
149 }
150
151 #[inline(always)]
152 fn expose_secret_mut(&mut self) -> &mut [T; N] {
153 &mut self.inner
154 }
155}
156
157#[cfg(feature = "rand")]
158impl<const N: usize> Fixed<[u8; N]> {
159 #[inline]
182 pub fn from_random() -> Self {
183 let mut bytes = [0u8; N];
184 SysRng
185 .try_fill_bytes(&mut bytes)
186 .expect("SysRng failure is a program error");
187 Self::from(bytes)
188 }
189}
190
191#[cfg(feature = "encoding-hex")]
192impl<const N: usize> Fixed<[u8; N]> {
193 pub fn try_from_hex(hex: &str) -> Result<Self, crate::error::HexError> {
210 let bytes = zeroize::Zeroizing::new(hex.try_from_hex()?);
211 if bytes.len() != N {
212 #[cfg(debug_assertions)]
213 return Err(crate::error::HexError::InvalidLength {
214 expected: N,
215 got: bytes.len(),
216 });
217 #[cfg(not(debug_assertions))]
218 return Err(crate::error::HexError::InvalidLength);
219 }
220 let mut arr = [0u8; N];
221 arr.copy_from_slice(&bytes);
222 Ok(Self::new(arr))
223 }
224}
225
226#[cfg(feature = "encoding-base64")]
227impl<const N: usize> Fixed<[u8; N]> {
228 pub fn try_from_base64url(s: &str) -> Result<Self, crate::error::Base64Error> {
245 let bytes = zeroize::Zeroizing::new(s.try_from_base64url()?);
246 if bytes.len() != N {
247 #[cfg(debug_assertions)]
248 return Err(crate::error::Base64Error::InvalidLength {
249 expected: N,
250 got: bytes.len(),
251 });
252 #[cfg(not(debug_assertions))]
253 return Err(crate::error::Base64Error::InvalidLength);
254 }
255 let mut arr = [0u8; N];
256 arr.copy_from_slice(&bytes);
257 Ok(Self::new(arr))
258 }
259}
260
261#[cfg(feature = "encoding-bech32")]
262impl<const N: usize> Fixed<[u8; N]> {
263 pub fn try_from_bech32_unchecked(s: &str) -> Result<Self, crate::error::Bech32Error> {
271 let (_hrp, bytes_raw) = s.try_from_bech32_unchecked()?;
272 let bytes = zeroize::Zeroizing::new(bytes_raw);
273 if bytes.len() != N {
274 #[cfg(debug_assertions)]
275 return Err(crate::error::Bech32Error::InvalidLength {
276 expected: N,
277 got: bytes.len(),
278 });
279 #[cfg(not(debug_assertions))]
280 return Err(crate::error::Bech32Error::InvalidLength);
281 }
282 let mut arr = [0u8; N];
283 arr.copy_from_slice(&bytes);
284 Ok(Self::new(arr))
285 }
286
287 pub fn try_from_bech32(s: &str, expected_hrp: &str) -> Result<Self, crate::error::Bech32Error> {
293 let bytes_raw = s.try_from_bech32(expected_hrp)?;
294 let bytes = zeroize::Zeroizing::new(bytes_raw);
295 if bytes.len() != N {
296 #[cfg(debug_assertions)]
297 return Err(crate::error::Bech32Error::InvalidLength {
298 expected: N,
299 got: bytes.len(),
300 });
301 #[cfg(not(debug_assertions))]
302 return Err(crate::error::Bech32Error::InvalidLength);
303 }
304 let mut arr = [0u8; N];
305 arr.copy_from_slice(&bytes);
306 Ok(Self::new(arr))
307 }
308}
309
310#[cfg(feature = "encoding-bech32m")]
311impl<const N: usize> Fixed<[u8; N]> {
312 pub fn try_from_bech32m_unchecked(s: &str) -> Result<Self, crate::error::Bech32Error> {
320 let (_hrp, bytes_raw) = s.try_from_bech32m_unchecked()?;
321 let bytes = zeroize::Zeroizing::new(bytes_raw);
322 if bytes.len() != N {
323 #[cfg(debug_assertions)]
324 return Err(crate::error::Bech32Error::InvalidLength {
325 expected: N,
326 got: bytes.len(),
327 });
328 #[cfg(not(debug_assertions))]
329 return Err(crate::error::Bech32Error::InvalidLength);
330 }
331 let mut arr = [0u8; N];
332 arr.copy_from_slice(&bytes);
333 Ok(Self::new(arr))
334 }
335
336 pub fn try_from_bech32m(s: &str, expected_hrp: &str) -> Result<Self, crate::error::Bech32Error> {
342 let bytes_raw = s.try_from_bech32m(expected_hrp)?;
343 let bytes = zeroize::Zeroizing::new(bytes_raw);
344 if bytes.len() != N {
345 #[cfg(debug_assertions)]
346 return Err(crate::error::Bech32Error::InvalidLength {
347 expected: N,
348 got: bytes.len(),
349 });
350 #[cfg(not(debug_assertions))]
351 return Err(crate::error::Bech32Error::InvalidLength);
352 }
353 let mut arr = [0u8; N];
354 arr.copy_from_slice(&bytes);
355 Ok(Self::new(arr))
356 }
357}
358
359#[cfg(feature = "ct-eq")]
360impl<T: zeroize::Zeroize> crate::ConstantTimeEq for Fixed<T>
361where
362 T: crate::ConstantTimeEq,
363{
364 fn ct_eq(&self, other: &Self) -> bool {
365 self.inner.ct_eq(&other.inner)
366 }
367}
368
369impl<T: zeroize::Zeroize> core::fmt::Debug for Fixed<T> {
370 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
371 f.write_str("[REDACTED]")
372 }
373}
374
375#[cfg(feature = "cloneable")]
376impl<T: zeroize::Zeroize + crate::CloneableSecret> Clone for Fixed<T> {
377 fn clone(&self) -> Self {
378 Self::new(self.inner.clone())
379 }
380}
381
382#[cfg(feature = "serde-serialize")]
383impl<T: zeroize::Zeroize + crate::SerializableSecret> serde::Serialize for Fixed<T> {
384 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
385 where
386 S: serde::Serializer,
387 {
388 self.inner.serialize(serializer)
389 }
390}
391
392#[cfg(feature = "serde-deserialize")]
393impl<'de, const N: usize> serde::Deserialize<'de> for Fixed<[u8; N]> {
394 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
395 where
396 D: serde::Deserializer<'de>,
397 {
398 use core::fmt;
399 use serde::de::Visitor;
400 struct FixedVisitor<const M: usize>;
401 impl<'de, const M: usize> Visitor<'de> for FixedVisitor<M> {
402 type Value = Fixed<[u8; M]>;
403 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
404 write!(formatter, "a byte array of length {}", M)
405 }
406 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
407 where
408 A: serde::de::SeqAccess<'de>,
409 {
410 let mut vec: zeroize::Zeroizing<alloc::vec::Vec<u8>> =
411 zeroize::Zeroizing::new(alloc::vec::Vec::with_capacity(M));
412 while let Some(value) = seq.next_element()? {
413 vec.push(value);
414 }
415 if vec.len() != M {
416 #[cfg(debug_assertions)]
417 return Err(serde::de::Error::invalid_length(
418 vec.len(),
419 &M.to_string().as_str(),
420 ));
421 #[cfg(not(debug_assertions))]
422 return Err(serde::de::Error::custom("decoded length mismatch"));
423 }
424 let mut arr = [0u8; M];
425 arr.copy_from_slice(&vec);
426 Ok(Fixed::new(arr))
427 }
428 }
429 deserializer.deserialize_seq(FixedVisitor::<N>)
430 }
431}
432
433impl<T: zeroize::Zeroize> zeroize::Zeroize for Fixed<T> {
435 fn zeroize(&mut self) {
436 self.inner.zeroize();
437 }
438}
439
440impl<T: zeroize::Zeroize> Drop for Fixed<T> {
441 fn drop(&mut self) {
442 self.zeroize();
443 }
444}
445
446impl<T: zeroize::Zeroize> zeroize::ZeroizeOnDrop for Fixed<T> {}