# 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 that forwards to the inner type.
- When the `zeroize` feature is enabled, `FixedZeroizing<T>` and `DynamicZeroizing<T>` provide automatic zeroing on drop.

## Installation

```toml
[dependencies]
secure-gate = "0.5.4"
```

With automatic zeroing (recommended for most use cases):

```toml
secure-gate = { version = "0.5.4", features = ["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

```rust
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();

// New in 0.5.4: direct slice conversion (zero-cost!)
let key_slice: &[u8] = key.as_ref();            // Immutable slice
let mut key_mut: &mut [u8] = key.as_mut();      // Mutable slice
key_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>

// 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));
```

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

- All zeroing uses `zeroize::Zeroize` (volatile writes + compiler fence).
- `Vec<u8>` and `String` have 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`.

## Macros

```rust
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>)
```

## Example Aliases

```rust
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();
```

### 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** (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.

[View full interactive report](https://slurp9187.github.io/secure_gate/benches/fixed_vs_raw/report/)

## Migration from v0.4.x

- `SecureGate<T>` → `Fixed<T>` (stack) or `Dynamic<T>` (heap)
- `.expose_secret()` → direct deref (`&*value` or `&mut *value`) for `Fixed`/`Dynamic`
- Automatic zeroing → `FixedZeroizing<T>` or `DynamicZeroizing<T>` (with `zeroize` feature)

## Changelog

See [CHANGELOG.md](https://github.com/Slurp9187/secure_gate/blob/main/CHANGELOG.md)

## License

MIT OR Apache-2.0