secure-gate
Zero-cost, no_std-compatible wrappers for handling sensitive data in memory.
Fixed<T>– stack-allocated, zero-cost wrapper.Dynamic<T>– heap-allocated wrapper with full.into()ergonomics.- When the
zeroizefeature is enabled,FixedZeroizing<T>andDynamicZeroizing<T>provide automatic zeroing on drop.
Installation
[]
= "0.5.6"
With automatic zeroing (recommended for most use cases):
= { = "0.5.6", = ["zeroize"] }
Features
| 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 |
Quick Start
use ;
// Beautiful type aliases
fixed_alias!;
fixed_alias!;
dynamic_alias!;
dynamic_alias!;
// Fixed-size secrets — natural syntax
let key: Aes256Key = rng.gen.into; // The dream
let nonce: Nonce12 = rng.gen.into;
// Heap-based secrets — now just as beautiful!
let pw: Password = "hunter2".into; // From<&str>
let pw2: Password = "hunter2".to_string.into; // From<String>
let jwt: JwtKey = secret_bytes.into; // From<Vec<u8>>
// Zeroizing heap secrets — same ergonomics
let temp_pw: = "temp123".into;
let temp_key: = vec!.into;
// Access is explicit and loud
let bytes: & = key.expose_secret;
let pw_str: &str = pw.expose_secret;
Memory Guarantees (zeroize feature enabled)
| 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 |
Important: DynamicZeroizing<T> is accessed via .expose_secret() — it does not implement Deref.
Macros
// Fixed-size secrets
secure! // → Fixed<[u8; 32]>
// Heap secrets (non-zeroizing)
secure! // → Dynamic<String>
secure! // → Dynamic<Vec<u8>>
secure! // → Dynamic<Vec<u8>>
// Zeroizing secrets (zeroize feature)
secure_zeroizing! // → FixedZeroizing<[u8; 32]>
secure_zeroizing! // → DynamicZeroizing<String>
// Type aliases — the recommended way
fixed_alias!
dynamic_alias!
Example Aliases
fixed_alias!;
fixed_alias!;
fixed_alias!;
dynamic_alias!;
dynamic_alias!;
// Usage — pure joy
let key: Aes256Key = rng.gen.into;
let pw: Password = "hunter2".into;
let jwt: JwtSigningKey = secret_bytes.into;
Zero-cost — proven on real hardware
| Implementation | Median time | Max overhead vs raw |
|---|---|---|
raw [u8; 32] |
~460 ps | — |
Fixed<[u8; 32]> |
~460 ps | +28 ps |
fixed_alias!(Key, 32) |
~475 ps | +13 ps |
Test machine (2019-era laptop):
Lenovo ThinkPad L13 • Intel Core i7-10510U • 16 GB RAM • Windows 10 Pro
Measured with Criterion under real-world load.
Overhead is < 0.1 CPU cycles — indistinguishable from raw arrays.
Migration from v0.4.x
SecureGate<T>→Fixed<T>(stack) orDynamic<T>(heap).expose_secret()→value.expose_secret().expose_secret_mut()→value.expose_secret_mut()- Automatic zeroing →
FixedZeroizing<T>orDynamicZeroizing<T>
Note: .view() and .view_mut() are deprecated in v0.5.5 and will be removed in v0.6.0.
Changelog
License
MIT OR Apache-2.0