Zero-cost, no_std-compatible wrappers for handling sensitive data in memory.
Fixed<T>– stack-allocated, zero-cost wrapper.Dynamic<T>– heap-allocated wrapper that forwards to the inner type.- When the
zeroizefeature is enabled,FixedZeroizing<T>andDynamicZeroizing<T>provide automatic zeroing on drop.
[dependencies]
secure-gate = "0.5.5"With automatic zeroing (recommended for most use cases):
secure-gate = { version = "0.5.5", features = ["zeroize"] }| Feature | Description |
|---|---|
zeroize |
Enables zeroize integration (Zeroizing, SecretBox) |
serde |
Optional serialization support |
alloc |
Required for Dynamic<T> (enabled by default) |
std |
Not required – works in no_std environments |
use secure_gate::{secure, fixed_alias, dynamic_alias};
// Type aliases
fixed_alias!(Aes256Key, 32);
fixed_alias!(Nonce12, 12);
dynamic_alias!(Password, String);
// Construction — idiomatic and zero-cost
let key = Aes256Key::from(rng.gen()); // explicit
let key2: Aes256Key = rng.gen().into(); // natural .into()
let key3 = Aes256Key::new(rng.gen()); // classic
let nonce = Nonce12::from(rng.gen());
let nonce2: Nonce12 = rng.gen().into();
// Access the secret — loud, clear, and zero-cost
let key_bytes: &[u8] = key.expose_secret();
let key_bytes_mut: &mut [u8] = key.expose_secret_mut();
key_bytes_mut[0] = 0xFF; // e.g. key scheduling, HKDF, etc.
assert_eq!(key[0], 0xFF);
// Heap-based secrets
let mut password: Password = secure!(String, "hunter2".to_string());
password.push('!');
password.finish_mut(); // shrink_to_fit() on String/Vec<u8>
// Access the secret directly
let pw_str: &str = password.expose_secret();
let pw_str_mut: &mut str = password.expose_secret_mut().as_mut_str();
pw_str_mut.push('!');
// Auto-zeroing variants (requires `zeroize` feature)
let temp_key = secure_gate::secure_zeroizing!([u8; 32], derive_key());
let secret_vec = secure_gate::secure_zeroizing!(heap Vec<u8>, Box::new(secret_bytes));| Type | Allocation | Auto-zero on drop | Full capacity wiped | Slack memory eliminated | Notes |
|---|---|---|---|---|---|
Fixed<T> |
Stack | Yes (via Zeroizing) |
Yes | Yes (no heap) | No allocation |
Dynamic<T> |
Heap | Yes (via SecretBox) |
Yes | No (until drop) | Use finish_mut() to shrink |
FixedZeroizing<T> |
Stack | Yes | Yes | Yes | RAII wrapper |
DynamicZeroizing<T> |
Heap | Yes | Yes | No (until drop) | SecretBox prevents copies |
- All zeroing uses
zeroize::Zeroize(volatile writes + compiler fence). Vec<u8>andStringhave their full current capacity zeroed and are truncated to length zero.- The underlying allocation is freed on drop (standard Rust behavior); capacity is not forcibly reduced unless
finish_mut()/shrink_to_fit()is called. - Past reallocations may leave copies of data elsewhere in memory. Pre-allocate with the final expected size to avoid reallocations.
Important: DynamicZeroizing<T> (i.e. SecretBox<T>) is accessed via .expose_secret() and .expose_secret_mut() — it does not implement Deref.
secure!([u8; 32], rng.gen()) // Fixed<[u8; 32]>
secure!(String, "pw".into()) // Dynamic<String>
secure!(Vec<u8>, data.to_vec()) // Dynamic<Vec<u8>>
secure_zeroizing!([u8; 32], key) // FixedZeroizing<[u8; 32]> (zeroize feature)
secure_zeroizing!(heap Vec<u8>, Box::new(data)) // DynamicZeroizing<Vec<u8>>
fixed_alias!(MyKey, 32)
dynamic_alias!(MySecret, Vec<u8>)fixed_alias!(Aes128Key, 16);
fixed_alias!(Aes256Key, 32);
fixed_alias!(XChaCha20Nonce, 24);
dynamic_alias!(Password, String);
dynamic_alias!(JwtSigningKey, Vec<u8>);
// Usage
let key = Aes256Key::from(rng.gen());
let key2: Aes256Key = rng.gen().into();| Implementation | Median time | Max overhead vs raw |
|---|---|---|
raw [u8; 32] |
~460 ps | — |
Fixed<[u8; 32]> |
~460 ps | +28 ps (0.000000028 s) |
fixed_alias!(Key, 32) |
~475 ps | +13 ps |
Test machine (2019-era laptop):
Lenovo ThinkPad L13 (81XH) • Intel Core i7-10510U (4c/8t @ 1.80 GHz) • 16 GB RAM • Windows 10 Pro 26100.1
Measured with Criterion 0.5 under real-world load (including Windows Update check).
Even under background load, overhead is < 0.1 CPU cycles — indistinguishable from raw arrays.
SecureGate<T>→Fixed<T>(stack) orDynamic<T>(heap).expose_secret()→value.expose_secret()(returns&T).expose_secret_mut()→value.expose_secret_mut()(returns&mut T)- Automatic zeroing →
FixedZeroizing<T>orDynamicZeroizing<T>(withzeroizefeature)
Note: .view() and .view_mut() are deprecated in v0.5.5 and will be removed in v0.6.0.
See CHANGELOG.md
MIT OR Apache-2.0