Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
secure-gate
A zero-overhead, no_std-compatible secret wrapper with automatic zeroization.
Key Features Summary
secure-gate is designed for seamless, safe secret handling with zero runtime overhead when features are disabled. Core highlights:
- Auto-Gating: Switches between
SecretBox<T>+ zeroization (defaultzeroizefeature) and plainBox<T>fallback for minimal builds—no code changes required. - No-Std Native: Full
no_std+allocsupport for embedded systems. - Safe & Ergonomic: All public API in 100% safe Rust;
secure!macro for quick construction; aliases likeSecurePassword(immutable default),SecurePasswordBuilder(mutable opt-in), andSecureKey32. - Redacted & Zeroized: Automatic
Debugredaction ("[REDACTED]"); best-effort zeroization on drop/mutation viazeroize. - Serde-Ready: Opt-in serialization of secrets (explicitly exposes the secret in serialized form, e.g., JSON strings; protect output bytes appropriately).
- Fuzz-Hardened: 5 libFuzzer targets running 300 CPU minutes nightly.
- Zero-Alloc Fixed Secrets: Stack-only types for keys/nonces (default via
stackfeature) — no heap, cache-local,#![no_global_oom]friendly.
Installation
Add this to your Cargo.toml:
[]
= "0.3.2"
Usage
use ;
let password: SecurePassword = "hunter2".into; // Immutable default, zeroized on drop
let key = new; // generic fixed-size (heap for dynamic fallbacks)
let token = new; // dynamic buffer
// Scoped mutation (preferred for mutable cases)
let mut pw_mut: SecurePasswordBuilder = new;
pw_mut.expose_mut.expose_secret_mut.push_str;
pw_mut.finish_mut; // reduce excess capacity via shrink_to_fit (best-effort; no scrub of freed memory)
// Convert to immutable form (zeroizes the builder)
let pw: SecurePassword = pw_mut.into_password; // or .build()
// Extraction (use sparingly)
let bytes: = token.into_inner; // original zeroized immediately
Note: SecurePasswordMut is deprecated in v0.3.1 — use SecurePasswordBuilder instead.
Zero-Allocation Fixed-Size Secrets (Default)
For keys/nonces/IVs, aliases like SecureKey32 default to stack-only Zeroizing<[u8; N]> — no heap, deterministic, side-channel minimal:
use ;
use ;
static AES_KEY: SecureKey32 = key32; // const-eligible!
let key: SecureKey32 = new; // from RNG
let nonce: SecureNonce12 = nonce12;
// Access: deref to inner slice/array
assert_eq!;
Falls back to Secure<[u8; N]> if stack disabled. Ideal for crypto hot paths (rustls/ring-style).
Fuzzing Configuration
| Target | Description | Runtime per CI run |
|---|---|---|
expose |
Memory access + finish_mut |
60 minutes |
clone |
init_with, into_inner, scoped zeroization |
60 minutes |
serde |
JSON + bincode deserialization from untrusted input | 60 minutes |
parsing |
FromStr parsing |
60 minutes |
mut |
Unbounded expose_mut() mutation stress |
60 minutes |
- 5 libFuzzer targets running nightly
- 300 CPU minutes of continuous fuzzing on GitHub Actions
Dependencies
[]
= { = "0.10.3", = true, = false }
= { = "1.8", = true, = false, = [
"alloc",
"zeroize_derive",
] }
= { = "1.0", = ["derive"], = true }
Features
| Feature | Effect |
|---|---|
zeroize |
Enables SecretBox<T> + zeroization on drop (default) |
stack |
Zero-alloc fixed-size types (Zeroizing<[u8; N]> for SecureKey32 etc.; default) |
serde |
Adds Serialize / Deserialize impls |
unsafe-wipe |
Opt-in fast zeroization for Secure<String> (no allocation, preserves len/cap; requires zeroize). Disables #![forbid(unsafe_code)] for this path—safe usage (only overwrites used buffer with zeros; null bytes valid UTF-8). Use for performance-critical secrets; stick to safe path otherwise. |
full |
Enables zeroize + serde + unsafe-wipe |
Zeroization Guarantees
Secure<T> provides best-effort memory zeroization on drop/mutation via the zeroize crate:
- What It Does: Explicitly overwrites secret bytes (up to
.len()for dynamic types likeVec/String) using volatile operations that resist compiler optimization. - Platform Coverage: Works on all stable Rust targets (x86, ARM, RISC-V, etc.) via portable intrinsics. No guarantees against hardware leaks (e.g., cache side-channels)—use constant-time primitives alongside.
- Limitations: Only affects the wrapped value; doesn't secure against copies, logs, or kernel dumps. For full protection, avoid extraction (
into_inner) and use scopedexpose_mut(). - finish_mut: After mutations, call this to reduce excess capacity (for
Vec<u8>,String) viashrink_to_fit(). This is best-effort—some allocators may not shrink—and does not overwrite freed memory (old secrets may persist until allocator/OS reuse). Zeroization on drop still covers only the used portion (up to.len()). - Dynamic Container Caveats: For growable types like
Vec<u8>orString, safe Rust cannot zero the full historical capacity (e.g., aftertruncateor realloc). Only the current slice up to.len()is overwritten on drop. Avoid patterns like filling a large buffer with secrets then truncating to small length—opt for fixed-size where possible or explicitly zero excess viaexpose_mut().fill(0)before shrinking. - Unsafe-Wipe Fast Path: When enabled,
Secure<String>usesunsafefor zero-allocation wiping (preserves len/cap)—safe for used buffer only. Null bytes are valid UTF-8; no invariants broken. Opt-in for performance (e.g., high-frequency secrets); safe path used otherwise (allocates temp zeros). - Fallback Mode: Disabled without
zeroizefeature—treat as plainBox<T>. - Stack Aliases Note: Fixed-size types like
SecureKey32useZeroizing<[u8; N]>by default (viastackfeature) for zero-overhead zeroization — same guarantees, no heap.
For details, see zeroize docs.
Contribution
Contributions welcome! Please submit PRs with tests/fuzz targets.
Changelog
See CHANGELOG.md for detailed release notes.
License
Licensed under MIT OR Apache-2.0