1#[cfg(feature = "alloc")]
24extern crate alloc;
25use alloc::boxed::Box;
26use zeroize::Zeroize;
27
28#[cfg(any(feature = "encoding-hex", feature = "encoding-base64"))]
29use crate::RevealSecret;
30
31#[cfg(feature = "encoding-base64")]
33use crate::traits::encoding::base64_url::ToBase64Url;
34#[cfg(feature = "encoding-hex")]
35use crate::traits::encoding::hex::ToHex;
36
37#[cfg(feature = "rand")]
38use rand::{rngs::OsRng, TryRngCore};
39
40#[cfg(feature = "encoding-base64")]
41use crate::traits::decoding::base64_url::FromBase64UrlStr;
42#[cfg(feature = "encoding-bech32")]
43use crate::traits::decoding::bech32::FromBech32Str;
44#[cfg(feature = "encoding-bech32m")]
45use crate::traits::decoding::bech32m::FromBech32mStr;
46#[cfg(feature = "encoding-hex")]
47use crate::traits::decoding::hex::FromHexStr;
48
49pub struct Dynamic<T: ?Sized + zeroize::Zeroize> {
56 inner: Box<T>,
57}
58
59impl<T: ?Sized + zeroize::Zeroize> Dynamic<T> {
60 #[doc(alias = "from")]
61 #[inline(always)]
62 pub fn new<U>(value: U) -> Self
63 where
64 U: Into<Box<T>>,
65 {
66 let inner = value.into();
67 Self { inner }
68 }
69}
70
71impl<T: ?Sized + zeroize::Zeroize> From<Box<T>> for Dynamic<T> {
73 #[inline(always)]
74 fn from(boxed: Box<T>) -> Self {
75 Self { inner: boxed }
76 }
77}
78
79impl From<&[u8]> for Dynamic<Vec<u8>> {
80 #[inline(always)]
81 fn from(slice: &[u8]) -> Self {
82 Self::new(slice.to_vec())
83 }
84}
85
86impl From<&str> for Dynamic<String> {
87 #[inline(always)]
88 fn from(input: &str) -> Self {
89 Self::new(input.to_string())
90 }
91}
92
93impl<T: 'static + zeroize::Zeroize> From<T> for Dynamic<T> {
94 #[inline(always)]
95 fn from(value: T) -> Self {
96 Self {
97 inner: Box::new(value),
98 }
99 }
100}
101
102impl Dynamic<Vec<u8>> {
104 #[cfg(feature = "encoding-hex")]
105 #[inline]
106 pub fn to_hex(&self) -> alloc::string::String {
107 self.with_secret(|s: &Vec<u8>| s.to_hex())
108 }
109
110 #[cfg(feature = "encoding-hex")]
111 #[inline]
112 pub fn to_hex_upper(&self) -> alloc::string::String {
113 self.with_secret(|s: &Vec<u8>| s.to_hex_upper())
114 }
115
116 #[cfg(feature = "encoding-base64")]
117 #[inline]
118 pub fn to_base64url(&self) -> alloc::string::String {
119 self.with_secret(|s: &Vec<u8>| s.to_base64url())
120 }
121
122 #[cfg(any(
137 feature = "encoding-hex",
138 feature = "encoding-base64",
139 feature = "encoding-bech32",
140 feature = "encoding-bech32m",
141 ))]
142 #[inline(always)]
143 fn from_protected_bytes(mut protected: zeroize::Zeroizing<alloc::vec::Vec<u8>>) -> Self {
144 let mut boxed = Box::new(alloc::vec::Vec::new());
146 core::mem::swap(&mut *boxed, &mut *protected);
147 Self::from(boxed)
148 }
149}
150
151impl crate::RevealSecret for Dynamic<String> {
153 type Inner = String;
154
155 #[inline(always)]
156 fn with_secret<F, R>(&self, f: F) -> R
157 where
158 F: FnOnce(&String) -> R,
159 {
160 f(&self.inner)
161 }
162
163 #[inline(always)]
164 fn expose_secret(&self) -> &String {
165 &self.inner
166 }
167
168 #[inline(always)]
169 fn len(&self) -> usize {
170 self.inner.len()
171 }
172}
173
174impl<T: zeroize::Zeroize> crate::RevealSecret for Dynamic<Vec<T>> {
175 type Inner = Vec<T>;
176
177 #[inline(always)]
178 fn with_secret<F, R>(&self, f: F) -> R
179 where
180 F: FnOnce(&Vec<T>) -> R,
181 {
182 f(&self.inner)
183 }
184
185 #[inline(always)]
186 fn expose_secret(&self) -> &Vec<T> {
187 &self.inner
188 }
189
190 #[inline(always)]
191 fn len(&self) -> usize {
192 self.inner.len() * core::mem::size_of::<T>()
193 }
194}
195
196impl crate::RevealSecretMut for Dynamic<String> {
198 #[inline(always)]
199 fn with_secret_mut<F, R>(&mut self, f: F) -> R
200 where
201 F: FnOnce(&mut String) -> R,
202 {
203 f(&mut self.inner)
204 }
205
206 #[inline(always)]
207 fn expose_secret_mut(&mut self) -> &mut String {
208 &mut self.inner
209 }
210}
211
212impl<T: zeroize::Zeroize> crate::RevealSecretMut for Dynamic<Vec<T>> {
213 #[inline(always)]
214 fn with_secret_mut<F, R>(&mut self, f: F) -> R
215 where
216 F: FnOnce(&mut Vec<T>) -> R,
217 {
218 f(&mut self.inner)
219 }
220
221 #[inline(always)]
222 fn expose_secret_mut(&mut self) -> &mut Vec<T> {
223 &mut self.inner
224 }
225}
226
227#[cfg(feature = "rand")]
229impl Dynamic<alloc::vec::Vec<u8>> {
230 #[inline]
231 pub fn from_random(len: usize) -> Self {
232 let mut bytes = vec![0u8; len];
233 OsRng
234 .try_fill_bytes(&mut bytes)
235 .expect("OsRng failure is a program error");
236 Self::from(bytes)
237 }
238}
239
240#[cfg(feature = "encoding-hex")]
242impl Dynamic<alloc::vec::Vec<u8>> {
243 pub fn try_from_hex(s: &str) -> Result<Self, crate::error::HexError> {
248 Ok(Self::from_protected_bytes(zeroize::Zeroizing::new(
249 s.try_from_hex()?,
250 )))
251 }
252}
253
254#[cfg(feature = "encoding-base64")]
255impl Dynamic<alloc::vec::Vec<u8>> {
256 pub fn try_from_base64url(s: &str) -> Result<Self, crate::error::Base64Error> {
261 Ok(Self::from_protected_bytes(zeroize::Zeroizing::new(
262 s.try_from_base64url()?,
263 )))
264 }
265}
266
267#[cfg(feature = "encoding-bech32")]
268impl Dynamic<alloc::vec::Vec<u8>> {
269 pub fn try_from_bech32_unchecked(s: &str) -> Result<Self, crate::error::Bech32Error> {
280 let (_hrp, bytes) = s.try_from_bech32_unchecked()?;
281 Ok(Self::from_protected_bytes(zeroize::Zeroizing::new(bytes)))
282 }
283
284 pub fn try_from_bech32(s: &str, expected_hrp: &str) -> Result<Self, crate::error::Bech32Error> {
293 Ok(Self::from_protected_bytes(zeroize::Zeroizing::new(
294 s.try_from_bech32(expected_hrp)?,
295 )))
296 }
297}
298
299#[cfg(feature = "encoding-bech32m")]
300impl Dynamic<alloc::vec::Vec<u8>> {
301 pub fn try_from_bech32m_unchecked(s: &str) -> Result<Self, crate::error::Bech32Error> {
312 let (_hrp, bytes) = s.try_from_bech32m_unchecked()?;
313 Ok(Self::from_protected_bytes(zeroize::Zeroizing::new(bytes)))
314 }
315
316 pub fn try_from_bech32m(
325 s: &str,
326 expected_hrp: &str,
327 ) -> Result<Self, crate::error::Bech32Error> {
328 Ok(Self::from_protected_bytes(zeroize::Zeroizing::new(
329 s.try_from_bech32m(expected_hrp)?,
330 )))
331 }
332}
333
334#[cfg(feature = "ct-eq")]
336impl<T: ?Sized + zeroize::Zeroize> crate::ConstantTimeEq for Dynamic<T>
337where
338 T: crate::ConstantTimeEq,
339{
340 fn ct_eq(&self, other: &Self) -> bool {
341 self.inner.ct_eq(&other.inner)
342 }
343}
344
345impl<T: ?Sized + zeroize::Zeroize> core::fmt::Debug for Dynamic<T> {
347 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
348 f.write_str("[REDACTED]")
349 }
350}
351
352#[cfg(feature = "cloneable")]
354impl<T: zeroize::Zeroize + crate::CloneableSecret> Clone for Dynamic<T> {
355 fn clone(&self) -> Self {
356 Self::new(self.inner.clone())
357 }
358}
359
360#[cfg(feature = "serde-serialize")]
362impl<T: zeroize::Zeroize + crate::SerializableSecret> serde::Serialize for Dynamic<T> {
363 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
364 where
365 S: serde::Serializer,
366 {
367 self.inner.serialize(serializer)
368 }
369}
370
371#[cfg(feature = "serde-deserialize")]
384pub const MAX_DESERIALIZE_BYTES: usize = 1_048_576;
385
386#[cfg(feature = "serde-deserialize")]
387impl Dynamic<alloc::vec::Vec<u8>> {
388 pub fn deserialize_with_limit<'de, D>(deserializer: D, limit: usize) -> Result<Self, D::Error>
402 where
403 D: serde::Deserializer<'de>,
404 {
405 let mut buf: zeroize::Zeroizing<alloc::vec::Vec<u8>> =
406 zeroize::Zeroizing::new(serde::Deserialize::deserialize(deserializer)?);
407 if buf.len() > limit {
408 return Err(serde::de::Error::custom(
410 "deserialized secret exceeds maximum size",
411 ));
412 }
413 let mut boxed = Box::new(alloc::vec::Vec::new());
415 core::mem::swap(&mut *boxed, &mut *buf);
416 Ok(Self::from(boxed))
417 }
418}
419
420#[cfg(feature = "serde-deserialize")]
421impl Dynamic<String> {
422 pub fn deserialize_with_limit<'de, D>(deserializer: D, limit: usize) -> Result<Self, D::Error>
436 where
437 D: serde::Deserializer<'de>,
438 {
439 let mut buf: zeroize::Zeroizing<alloc::string::String> =
440 zeroize::Zeroizing::new(serde::Deserialize::deserialize(deserializer)?);
441 if buf.len() > limit {
442 return Err(serde::de::Error::custom(
444 "deserialized secret exceeds maximum size",
445 ));
446 }
447 let mut boxed = Box::new(alloc::string::String::new());
449 core::mem::swap(&mut *boxed, &mut *buf);
450 Ok(Self::from(boxed))
451 }
452}
453
454#[cfg(feature = "serde-deserialize")]
455impl<'de> serde::Deserialize<'de> for Dynamic<alloc::vec::Vec<u8>> {
456 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
457 where
458 D: serde::Deserializer<'de>,
459 {
460 Self::deserialize_with_limit(deserializer, MAX_DESERIALIZE_BYTES)
461 }
462}
463
464#[cfg(feature = "serde-deserialize")]
465impl<'de> serde::Deserialize<'de> for Dynamic<String> {
466 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
467 where
468 D: serde::Deserializer<'de>,
469 {
470 Self::deserialize_with_limit(deserializer, MAX_DESERIALIZE_BYTES)
471 }
472}
473
474impl<T: ?Sized + zeroize::Zeroize> zeroize::Zeroize for Dynamic<T> {
476 fn zeroize(&mut self) {
477 self.inner.zeroize();
478 }
479}
480
481impl<T: ?Sized + zeroize::Zeroize> Drop for Dynamic<T> {
482 fn drop(&mut self) {
483 self.zeroize();
484 }
485}
486
487impl<T: ?Sized + zeroize::Zeroize> zeroize::ZeroizeOnDrop for Dynamic<T> {}