pub struct Dynamic<T: ?Sized + Zeroize> { /* private fields */ }Expand description
Heap-allocated secret wrapper with explicit access and automatic zeroization on drop.
Variable-length secrets (passwords, API keys, ciphertexts). Inner type must implement
Zeroize. Secret bytes live on the heap only — never on the stack. Requires alloc.
See Fixed<T> for the stack-allocated alternative.
use secure_gate::{Dynamic, RevealSecret};
let pw: Dynamic<String> = Dynamic::new(String::from("hunter2"));
assert_eq!(pw.with_secret(|s: &String| s.len()), 7);
assert_eq!(format!("{:?}", pw), "[REDACTED]");Zero-cost heap-allocated wrapper for variable-length secrets.
Dynamic<T> stores a T: Zeroize value in a Box<T> and unconditionally zeroizes
it on drop (including Vec/String spare capacity). There is no Deref, AsRef,
or Copy — every access is explicit through RevealSecret
or RevealSecretMut.
This is not Fixed<T> — it is the heap-allocated alternative for variable-length
secrets. Secret bytes never reside on the stack.
§Examples
use secure_gate::{Dynamic, RevealSecret};
let pw: Dynamic<String> = Dynamic::new(String::from("hunter2"));
assert_eq!(pw.with_secret(|s: &String| s.len()), 7);
assert_eq!(format!("{:?}", pw), "[REDACTED]");§Constructors for Dynamic<Vec<u8>>
| Constructor | Feature | Notes |
|---|---|---|
new(value) | — | Accepts Vec<u8>, &[u8], Box<Vec<u8>> |
new_with(f) | — | Scoped closure construction |
try_from_hex(s) | encoding-hex | Constant-time hex decoding |
try_from_base64url(s) | encoding-base64 | Constant-time Base64url decoding |
try_from_bech32(s, hrp) | encoding-bech32 | HRP-validated Bech32 |
try_from_bech32_unchecked(s) | encoding-bech32 | Bech32 without HRP check |
try_from_bech32m(s, hrp) | encoding-bech32m | HRP-validated Bech32m |
try_from_bech32m_unchecked(s) | encoding-bech32m | Bech32m without HRP check |
from_random(len) | rand | System RNG |
from_rng(len, rng) | rand | Custom RNG |
§See also
RevealSecret/RevealSecretMut— the 3-tier access traits.Fixed<T>— stack-allocated alternative.
Implementations§
Source§impl<T: ?Sized + Zeroize> Dynamic<T>
impl<T: ?Sized + Zeroize> Dynamic<T>
Sourcepub fn new<U>(value: U) -> Self
pub fn new<U>(value: U) -> Self
Wraps value in a Box<T> and returns a Dynamic<T>.
Accepts any type that implements Into<Box<T>> — including owned values,
Box<T>, String, Vec<u8>, &str (via the blanket From<&str> impl), etc.
Equivalent to Dynamic::from(value) — #[doc(alias = "from")] is set so both
names appear in docs.rs search.
Requires the alloc feature (which Dynamic<T> itself always requires).
Source§impl Dynamic<Vec<u8>>
impl Dynamic<Vec<u8>>
Sourcepub fn to_hex_upper(&self) -> String
pub fn to_hex_upper(&self) -> String
Encodes the secret bytes as an uppercase hex string.
Sourcepub fn to_hex_zeroizing(&self) -> EncodedSecret
pub fn to_hex_zeroizing(&self) -> EncodedSecret
Encodes the secret bytes as a lowercase hex string, returning
EncodedSecret to preserve zeroization.
Sourcepub fn to_hex_upper_zeroizing(&self) -> EncodedSecret
pub fn to_hex_upper_zeroizing(&self) -> EncodedSecret
Encodes the secret bytes as an uppercase hex string, returning
EncodedSecret to preserve zeroization.
Sourcepub fn try_from_hex(s: &str) -> Result<Self, HexError>
pub fn try_from_hex(s: &str) -> Result<Self, HexError>
Decodes a hex string (lowercase, uppercase, or mixed) into Dynamic<Vec<u8>>.
The decoded buffer is kept inside a Zeroizing wrapper until after the
Box allocation completes, guaranteeing zeroization even on OOM panic.
Source§impl Dynamic<Vec<u8>>
impl Dynamic<Vec<u8>>
Sourcepub fn to_base64url(&self) -> String
pub fn to_base64url(&self) -> String
Encodes the secret bytes as an unpadded Base64url string (RFC 4648, URL-safe alphabet).
Sourcepub fn to_base64url_zeroizing(&self) -> EncodedSecret
pub fn to_base64url_zeroizing(&self) -> EncodedSecret
Encodes the secret bytes as an unpadded Base64url string, returning
EncodedSecret to preserve zeroization.
Sourcepub fn try_from_base64url(s: &str) -> Result<Self, Base64Error>
pub fn try_from_base64url(s: &str) -> Result<Self, Base64Error>
Decodes a Base64url (unpadded) string into Dynamic<Vec<u8>>.
The decoded buffer is kept inside a Zeroizing wrapper until after the
Box allocation completes, guaranteeing zeroization even on OOM panic.
Source§impl Dynamic<Vec<u8>>
impl Dynamic<Vec<u8>>
Sourcepub fn try_to_bech32(&self, hrp: &str) -> Result<String, Bech32Error>
pub fn try_to_bech32(&self, hrp: &str) -> Result<String, Bech32Error>
Encodes the secret bytes as a Bech32 (BIP-173) string with the given HRP.
Sourcepub fn try_to_bech32_zeroizing(
&self,
hrp: &str,
) -> Result<EncodedSecret, Bech32Error>
pub fn try_to_bech32_zeroizing( &self, hrp: &str, ) -> Result<EncodedSecret, Bech32Error>
Encodes the secret bytes as a Bech32 string, returning
EncodedSecret to preserve zeroization.
Sourcepub fn try_from_bech32(s: &str, expected_hrp: &str) -> Result<Self, Bech32Error>
pub fn try_from_bech32(s: &str, expected_hrp: &str) -> Result<Self, Bech32Error>
Decodes a Bech32 (BIP-173) string into Dynamic<Vec<u8>>, validating the HRP
(case-insensitive).
The decoded buffer is kept inside a Zeroizing wrapper until after the
Box allocation completes, guaranteeing zeroization even on OOM panic.
HRP comparison is non-constant-time — this is intentional, as the HRP is public metadata, not secret material.
Sourcepub fn try_from_bech32_unchecked(s: &str) -> Result<Self, Bech32Error>
pub fn try_from_bech32_unchecked(s: &str) -> Result<Self, Bech32Error>
Decodes a Bech32 (BIP-173) string into Dynamic<Vec<u8>> without validating the HRP.
Use try_from_bech32 in security-critical code to prevent
cross-protocol confusion attacks.
Source§impl Dynamic<Vec<u8>>
impl Dynamic<Vec<u8>>
Sourcepub fn try_to_bech32m(&self, hrp: &str) -> Result<String, Bech32Error>
pub fn try_to_bech32m(&self, hrp: &str) -> Result<String, Bech32Error>
Encodes the secret bytes as a Bech32m (BIP-350) string with the given HRP.
Sourcepub fn try_to_bech32m_zeroizing(
&self,
hrp: &str,
) -> Result<EncodedSecret, Bech32Error>
pub fn try_to_bech32m_zeroizing( &self, hrp: &str, ) -> Result<EncodedSecret, Bech32Error>
Encodes the secret bytes as a Bech32m string, returning
EncodedSecret to preserve zeroization.
Sourcepub fn try_from_bech32m(
s: &str,
expected_hrp: &str,
) -> Result<Self, Bech32Error>
pub fn try_from_bech32m( s: &str, expected_hrp: &str, ) -> Result<Self, Bech32Error>
Decodes a Bech32m (BIP-350) string into Dynamic<Vec<u8>>, validating the HRP
(case-insensitive).
The decoded buffer is kept inside a Zeroizing wrapper until after the
Box allocation completes, guaranteeing zeroization even on OOM panic.
Sourcepub fn try_from_bech32m_unchecked(s: &str) -> Result<Self, Bech32Error>
pub fn try_from_bech32m_unchecked(s: &str) -> Result<Self, Bech32Error>
Decodes a Bech32m (BIP-350) string into Dynamic<Vec<u8>> without validating the HRP.
Use try_from_bech32m in security-critical code.
Source§impl Dynamic<String>
impl Dynamic<String>
Sourcepub fn new_with<F>(f: F) -> Self
pub fn new_with<F>(f: F) -> Self
Closure-based constructor for consistent API with Fixed::new_with.
The actual secret data is allocated on the heap; this method exists
for consistent security-first construction idiom across the crate.
Source§impl Dynamic<Vec<u8>>
impl Dynamic<Vec<u8>>
Sourcepub fn from_random(len: usize) -> Self
pub fn from_random(len: usize) -> Self
Fills a new Vec<u8> with len cryptographically secure random bytes and wraps it.
Uses the system RNG (OsRng) via TryRngCore::try_fill_bytes.
Requires the rand feature (and alloc, which Dynamic<Vec<u8>> always needs).
§Panics
Panics if the system RNG fails to provide bytes (TryRngCore::try_fill_bytes
returns Err). This is treated as a fatal environment error.
§Examples
use secure_gate::{Dynamic, RevealSecret};
let nonce: Dynamic<Vec<u8>> = Dynamic::from_random(24);
assert_eq!(nonce.len(), 24);Sourcepub fn from_rng<R: TryRngCore + TryCryptoRng>(
len: usize,
rng: &mut R,
) -> Result<Self, R::Error>
pub fn from_rng<R: TryRngCore + TryCryptoRng>( len: usize, rng: &mut R, ) -> Result<Self, R::Error>
Allocates a Vec<u8> of length len, fills it from rng, and wraps it.
Accepts any TryCryptoRng + TryRngCore — for example,
a seeded StdRng for deterministic tests. Requires the rand
feature and alloc (implicit — Dynamic<T> itself requires it).
§Errors
Returns R::Error if try_fill_bytes fails.
§Examples
use rand::rngs::StdRng;
use rand::SeedableRng;
use secure_gate::Dynamic;
let mut rng = StdRng::from_seed([9u8; 32]);
let nonce: Dynamic<Vec<u8>> = Dynamic::from_rng(24, &mut rng).expect("rng fill");Source§impl Dynamic<Vec<u8>>
impl Dynamic<Vec<u8>>
Sourcepub fn deserialize_with_limit<'de, D>(
deserializer: D,
limit: usize,
) -> Result<Self, D::Error>where
D: Deserializer<'de>,
pub fn deserialize_with_limit<'de, D>(
deserializer: D,
limit: usize,
) -> Result<Self, D::Error>where
D: Deserializer<'de>,
Deserializes into Dynamic<Vec<u8>>, rejecting payloads larger than limit bytes.
The standard serde::Deserialize impl calls this with MAX_DESERIALIZE_BYTES.
Use this method directly when you need a tighter or looser ceiling.
The intermediate buffer is kept inside a Zeroizing wrapper until after the Box
allocation completes, guaranteeing zeroization even on OOM panic. Oversized buffers
are also zeroized before the error is returned.
Important: this limit is enforced after the upstream deserializer has fully materialized the payload. It is a result-length acceptance bound, not a pre-allocation DoS guard. For untrusted input, enforce size limits at the transport or parser layer upstream.
Source§impl Dynamic<String>
impl Dynamic<String>
Sourcepub fn deserialize_with_limit<'de, D>(
deserializer: D,
limit: usize,
) -> Result<Self, D::Error>where
D: Deserializer<'de>,
pub fn deserialize_with_limit<'de, D>(
deserializer: D,
limit: usize,
) -> Result<Self, D::Error>where
D: Deserializer<'de>,
Deserializes into Dynamic<String>, rejecting payloads larger than limit bytes.
The standard serde::Deserialize impl calls this with MAX_DESERIALIZE_BYTES.
Use this method directly when you need a tighter or looser ceiling.
The intermediate buffer is kept inside a Zeroizing wrapper until after the Box
allocation completes, guaranteeing zeroization even on OOM panic. Oversized buffers
are also zeroized before the error is returned.
Important: this limit is enforced after the upstream deserializer has fully materialized the payload. It is a result-length acceptance bound, not a pre-allocation DoS guard. For untrusted input, enforce size limits at the transport or parser layer upstream.
Trait Implementations§
Source§impl<T: Zeroize + CloneableSecret> Clone for Dynamic<T>
Available on crate feature cloneable only.Opt-in cloning — requires cloneable feature and CloneableSecret
marker. Each clone is independently zeroized on drop, but cloning increases exposure surface.
impl<T: Zeroize + CloneableSecret> Clone for Dynamic<T>
cloneable only.Opt-in cloning — requires cloneable feature and CloneableSecret
marker. Each clone is independently zeroized on drop, but cloning increases exposure surface.
Source§impl<T> ConstantTimeEq for Dynamic<T>
Available on crate feature ct-eq only.Constant-time equality for Dynamic<T> — routes through expose_secret().
impl<T> ConstantTimeEq for Dynamic<T>
ct-eq only.Constant-time equality for Dynamic<T> — routes through expose_secret().
== is deliberately not implemented. Always use ct_eq.
Source§impl<T: ?Sized + Zeroize> Debug for Dynamic<T>
Always prints [REDACTED] — secrets never appear in debug output.
impl<T: ?Sized + Zeroize> Debug for Dynamic<T>
Always prints [REDACTED] — secrets never appear in debug output.
Source§impl<'de> Deserialize<'de> for Dynamic<String>
Available on crate feature serde-deserialize only.
impl<'de> Deserialize<'de> for Dynamic<String>
serde-deserialize only.Source§fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>where
D: Deserializer<'de>,
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>where
D: Deserializer<'de>,
Source§impl<'de> Deserialize<'de> for Dynamic<Vec<u8>>
Available on crate feature serde-deserialize only.
impl<'de> Deserialize<'de> for Dynamic<Vec<u8>>
serde-deserialize only.Source§fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>where
D: Deserializer<'de>,
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>where
D: Deserializer<'de>,
Source§impl<T: ?Sized + Zeroize> Drop for Dynamic<T>
Unconditionally zeroizes the inner value when the wrapper is dropped.
impl<T: ?Sized + Zeroize> Drop for Dynamic<T>
Unconditionally zeroizes the inner value when the wrapper is dropped.
Warning: Drop does not run under panic = "abort".
Source§impl<T: ?Sized + Zeroize> From<Box<T>> for Dynamic<T>
Zero-copy wrapping of an already-boxed value.
impl<T: ?Sized + Zeroize> From<Box<T>> for Dynamic<T>
Zero-copy wrapping of an already-boxed value.
Source§impl RevealSecret for Dynamic<String>
impl RevealSecret for Dynamic<String>
Source§fn into_inner(self) -> InnerSecret<String>
fn into_inner(self) -> InnerSecret<String>
Consumes self and returns the inner String wrapped in crate::InnerSecret.
Allocation note: allocates one small Box<String> sentinel (24 bytes on
64-bit) before the swap. If that allocation panics (OOM), self.inner is
unchanged and Dynamic::drop zeroizes the real secret during unwind —
confidentiality is preserved. This is the same OOM-safety pattern used by
from_protected_bytes and deserialize_with_limit.
See RevealSecret::into_inner for full documentation including the
redacted Debug behavior.
Source§fn with_secret<F, R>(&self, f: F) -> R
fn with_secret<F, R>(&self, f: F) -> R
Source§fn expose_secret(&self) -> &String
fn expose_secret(&self) -> &String
Source§impl<T: Zeroize> RevealSecret for Dynamic<Vec<T>>
impl<T: Zeroize> RevealSecret for Dynamic<Vec<T>>
Source§fn into_inner(self) -> InnerSecret<Vec<T>>
fn into_inner(self) -> InnerSecret<Vec<T>>
Consumes self and returns the inner Vec<T> wrapped in crate::InnerSecret.
Allocation note: allocates one small Box<Vec<T>> sentinel (24 bytes on
64-bit) before the swap. If that allocation panics (OOM), self.inner is
unchanged and Dynamic::drop zeroizes the real secret during unwind —
confidentiality is preserved. This is the same OOM-safety pattern used by
from_protected_bytes and deserialize_with_limit.
See RevealSecret::into_inner for full documentation including the
redacted Debug behavior.
Source§fn with_secret<F, R>(&self, f: F) -> R
fn with_secret<F, R>(&self, f: F) -> R
Source§fn expose_secret(&self) -> &Vec<T>
fn expose_secret(&self) -> &Vec<T>
Source§impl RevealSecretMut for Dynamic<String>
impl RevealSecretMut for Dynamic<String>
Source§impl<T: Zeroize + SerializableSecret> Serialize for Dynamic<T>
Available on crate feature serde-serialize only.Opt-in serialization — requires serde-serialize feature and
SerializableSecret marker. Serialization exposes the
full secret — audit every impl.
impl<T: Zeroize + SerializableSecret> Serialize for Dynamic<T>
serde-serialize only.Opt-in serialization — requires serde-serialize feature and
SerializableSecret marker. Serialization exposes the
full secret — audit every impl.
Source§impl<T: ?Sized + Zeroize> Zeroize for Dynamic<T>
Zeroizes the inner value (including Vec/String spare capacity).
impl<T: ?Sized + Zeroize> Zeroize for Dynamic<T>
Zeroizes the inner value (including Vec/String spare capacity).
Warning: does not run under panic = "abort".
impl<T: ?Sized + Zeroize> ZeroizeOnDrop for Dynamic<T>
Marker confirming that Dynamic<T> always zeroizes on drop.