Skip to main content

Dynamic

Struct Dynamic 

Source
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>>

ConstructorFeatureNotes
new(value)Accepts Vec<u8>, &[u8], Box<Vec<u8>>
new_with(f)Scoped closure construction
try_from_hex(s)encoding-hexConstant-time hex decoding
try_from_base64url(s)encoding-base64Constant-time Base64url decoding
try_from_bech32(s, hrp)encoding-bech32HRP-validated Bech32
try_from_bech32_unchecked(s)encoding-bech32Bech32 without HRP check
try_from_bech32m(s, hrp)encoding-bech32mHRP-validated Bech32m
try_from_bech32m_unchecked(s)encoding-bech32mBech32m without HRP check
from_random(len)randSystem RNG
from_rng(len, rng)randCustom RNG

§See also

Implementations§

Source§

impl<T: ?Sized + Zeroize> Dynamic<T>

Source

pub fn new<U>(value: U) -> Self
where U: Into<Box<T>>,

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>>

Source

pub fn to_hex(&self) -> String

Encodes the secret bytes as a lowercase hex string.

Source

pub fn to_hex_upper(&self) -> String

Encodes the secret bytes as an uppercase hex string.

Source

pub fn to_hex_zeroizing(&self) -> EncodedSecret

Encodes the secret bytes as a lowercase hex string, returning EncodedSecret to preserve zeroization.

Source

pub fn to_hex_upper_zeroizing(&self) -> EncodedSecret

Encodes the secret bytes as an uppercase hex string, returning EncodedSecret to preserve zeroization.

Source

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>>

Source

pub fn to_base64url(&self) -> String

Encodes the secret bytes as an unpadded Base64url string (RFC 4648, URL-safe alphabet).

Source

pub fn to_base64url_zeroizing(&self) -> EncodedSecret

Encodes the secret bytes as an unpadded Base64url string, returning EncodedSecret to preserve zeroization.

Source

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>>

Source

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.

Source

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.

Source

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.

Source

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>>

Source

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.

Source

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.

Source

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.

Source

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<Vec<u8>>

Construction helpers and random generation for Dynamic<Vec<u8>>.

Source

pub fn new_with<F>(f: F) -> Self
where F: FnOnce(&mut Vec<u8>),

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<String>

Source

pub fn new_with<F>(f: F) -> Self
where F: FnOnce(&mut String),

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>>

Source

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 (SysRng). Requires the rand feature (and alloc, which Dynamic<Vec<u8>> always needs).

§Panics

Panics if the system RNG fails to provide bytes (TryRng::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);
Source

pub fn from_rng<R: TryRng + 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 + TryRng — 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>>

Source

pub fn as_reader(&self) -> DynamicReader<'_>

Returns a DynamicReader that implements std::io::Read.

This replaces the common with_secret + Cursor boilerplate:

use std::io;
use secure_gate::Dynamic;

let secret = Dynamic::<Vec<u8>>::new(vec![1, 2, 3, 4]);

// Before: awkward closure + Cursor dance
// secret.with_secret(|b| io::copy(&mut Cursor::new(b), &mut encryptor))?;

// After: pipe directly into an encrypted writer — no intermediate buffer
let mut encryptor = io::sink(); // stand-in for a real encryptor
io::copy(&mut secret.as_reader(), &mut encryptor).unwrap();
§Security

Each read() call copies secret bytes into the caller’s buffer. Prefer piping directly into encrypted writers rather than reading into intermediate buffers. The caller is responsible for zeroizing any destination buffer.

Source§

impl Dynamic<Vec<u8>>

Source

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>

Source

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.

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<T> ConstantTimeEq for Dynamic<T>
where T: ConstantTimeEq + ?Sized + Zeroize, Self: RevealSecret<Inner = T>,

Available on crate feature ct-eq only.

Constant-time equality for Dynamic<T> — routes through expose_secret().

== is deliberately not implemented. Always use ct_eq.

Source§

fn ct_eq(&self, other: &Self) -> bool

Performs equality comparison in constant time. Read more
Source§

impl<T: ?Sized + Zeroize> Debug for Dynamic<T>

Always prints [REDACTED] — secrets never appear in debug output.

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for Dynamic<String>

Available on crate feature serde-deserialize only.
Source§

fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl<'de> Deserialize<'de> for Dynamic<Vec<u8>>

Available on crate feature serde-deserialize only.
Source§

fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

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§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

impl From<&[u8]> for Dynamic<Vec<u8>>

Copies a byte slice to the heap and wraps it.

Source§

fn from(slice: &[u8]) -> Self

Converts to this type from the input type.
Source§

impl From<&str> for Dynamic<String>

Copies a string to the heap and wraps it.

Source§

fn from(input: &str) -> Self

Converts to this type from the input type.
Source§

impl<T: ?Sized + Zeroize> From<Box<T>> for Dynamic<T>

Zero-copy wrapping of an already-boxed value.

Source§

fn from(boxed: Box<T>) -> Self

Converts to this type from the input type.
Source§

impl<T: 'static + Zeroize> From<T> for Dynamic<T>

Boxes the value and wraps it.

Source§

fn from(value: T) -> Self

Converts to this type from the input type.
Source§

impl RevealSecret for Dynamic<String>

Source§

fn into_inner(self) -> InnerSecret<String>
where Self: Sized, Self::Inner: Sized + Default + Zeroize,

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§

type Inner = String

The inner secret type being revealed. Read more
Source§

fn with_secret<F, R>(&self, f: F) -> R
where F: FnOnce(&String) -> R,

Provides scoped (recommended) read-only access to the secret. Read more
Source§

fn expose_secret(&self) -> &String

Returns a direct (auditable) read-only reference to the secret. Read more
Source§

fn len(&self) -> usize

Returns the length of the secret in bytes. Read more
Source§

fn is_empty(&self) -> bool

Returns true if the secret is empty. Read more
Source§

impl<T: Zeroize> RevealSecret for Dynamic<Vec<T>>

Source§

fn into_inner(self) -> InnerSecret<Vec<T>>
where Self: Sized, Self::Inner: Sized + Default + Zeroize,

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§

type Inner = Vec<T>

The inner secret type being revealed. Read more
Source§

fn with_secret<F, R>(&self, f: F) -> R
where F: FnOnce(&Vec<T>) -> R,

Provides scoped (recommended) read-only access to the secret. Read more
Source§

fn expose_secret(&self) -> &Vec<T>

Returns a direct (auditable) read-only reference to the secret. Read more
Source§

fn len(&self) -> usize

Returns the length of the secret in bytes. Read more
Source§

fn is_empty(&self) -> bool

Returns true if the secret is empty. Read more
Source§

impl RevealSecretMut for Dynamic<String>

Source§

fn with_secret_mut<F, R>(&mut self, f: F) -> R
where F: FnOnce(&mut String) -> R,

Provides scoped (recommended) mutable access to the secret. Read more
Source§

fn expose_secret_mut(&mut self) -> &mut String

Returns a direct (auditable) mutable reference to the secret. Read more
Source§

impl<T: Zeroize> RevealSecretMut for Dynamic<Vec<T>>

Source§

fn with_secret_mut<F, R>(&mut self, f: F) -> R
where F: FnOnce(&mut Vec<T>) -> R,

Provides scoped (recommended) mutable access to the secret. Read more
Source§

fn expose_secret_mut(&mut self) -> &mut Vec<T>

Returns a direct (auditable) mutable reference to the secret. Read more
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.

Source§

fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl Write for Dynamic<Vec<u8>>

Available on crate feature std only.

Streams bytes directly into the protected buffer via RevealSecretMut.

Data flows into the wrapper — this is a pure security improvement over accumulating plaintext in a bare Vec<u8> before wrapping.

§Example

use std::io::Write;
use secure_gate::Dynamic;

let mut secret = Dynamic::<Vec<u8>>::new(vec![]);
secret.write_all(b"decrypted payload").unwrap();

// Secret material was protected from the first byte —
// no intermediate unprotected buffer ever existed.
Source§

fn write(&mut self, buf: &[u8]) -> Result<usize>

Writes a buffer into this writer, returning how many bytes were written. Read more
Source§

fn flush(&mut self) -> Result<()>

Flushes this output stream, ensuring that all intermediately buffered contents reach their destination. Read more
1.36.0 · Source§

fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize, Error>

Like write, except that it writes from a slice of buffers. Read more
Source§

fn is_write_vectored(&self) -> bool

🔬This is a nightly-only experimental API. (can_vector)
Determines if this Writer has an efficient write_vectored implementation. Read more
1.0.0 · Source§

fn write_all(&mut self, buf: &[u8]) -> Result<(), Error>

Attempts to write an entire buffer into this writer. Read more
Source§

fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> Result<(), Error>

🔬This is a nightly-only experimental API. (write_all_vectored)
Attempts to write multiple buffers into this writer. Read more
1.0.0 · Source§

fn write_fmt(&mut self, args: Arguments<'_>) -> Result<(), Error>

Writes a formatted string into this writer, returning any error encountered. Read more
1.0.0 · Source§

fn by_ref(&mut self) -> &mut Self
where Self: Sized,

Creates a “by reference” adapter for this instance of Write. Read more
Source§

impl<T: ?Sized + Zeroize> Zeroize for Dynamic<T>

Zeroizes the inner value (including Vec/String spare capacity).

Warning: does not run under panic = "abort".

Source§

fn zeroize(&mut self)

Zero out this object from memory using Rust intrinsics which ensure the zeroization operation is not “optimized away” by the compiler.
Source§

impl<T: ?Sized + Zeroize> ZeroizeOnDrop for Dynamic<T>

Marker confirming that Dynamic<T> always zeroizes on drop.

Auto Trait Implementations§

§

impl<T> Freeze for Dynamic<T>
where T: ?Sized,

§

impl<T> RefUnwindSafe for Dynamic<T>
where T: RefUnwindSafe + ?Sized,

§

impl<T> Send for Dynamic<T>
where T: Send + ?Sized,

§

impl<T> Sync for Dynamic<T>
where T: Sync + ?Sized,

§

impl<T> Unpin for Dynamic<T>
where T: ?Sized,

§

impl<T> UnsafeUnpin for Dynamic<T>
where T: ?Sized,

§

impl<T> UnwindSafe for Dynamic<T>
where T: UnwindSafe + ?Sized,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,