Crate secure_gate

Crate secure_gate 

Source
Expand description

§secure-gate

no_std-compatible wrappers for sensitive data with explicit exposure requirements.

  • Fixed<T> — Stack-allocated wrapper
  • Dynamic<T> — Heap-allocated wrapper
  • FixedRng<N> — Cryptographically secure random bytes of fixed length N
  • DynamicRng — Heap-allocated cryptographically secure random bytes
  • HexString — Validated lowercase hexadecimal string wrapper
  • Base64String — Validated URL-safe base64 string wrapper (no padding)

With the zeroize feature enabled, memory containing secrets is zeroed on drop, including spare capacity where applicable.

Access to secret data requires an explicit .expose_secret() call. There are no Deref implementations or other implicit access paths.

Cloning is opt-in via the CloneableSecret trait.

§Installation

[dependencies]
secure-gate = "0.7.0-rc.1"

Recommended configuration:

secure-gate = { version = "0.7.0-rc.1", features = ["full"] }

§Features

FeatureDescription
zeroizeMemory zeroing on drop and opt-in cloning via CloneableSecret
randRandom generation (FixedRng<N>::generate(), DynamicRng::generate())
ct-eqConstant-time equality comparison
encodingEncoding support (encoding-hex + encoding-base64)
encoding-hexHex encoding, HexString, FixedRng hex methods
encoding-base64Base64String
fullAll optional features

The crate is no_std-compatible with alloc. Features are optional and add no overhead when unused.

§Quick Start

use secure_gate::{fixed_alias, dynamic_alias};

fixed_alias!(pub Aes256Key, 32);
dynamic_alias!(pub Password, String);

let pw: Password = "hunter2".into();
assert_eq!(pw.expose_secret(), "hunter2");

#[cfg(feature = "zeroize")]
{
    let key1: Aes256Key = Aes256Key::new([0u8; 32]);
    let key2 = key1.clone();
}

#[cfg(feature = "rand")]
{
    use secure_gate::fixed_alias_rng;

    fixed_alias_rng!(pub MasterKey, 32);
    fixed_alias_rng!(pub Nonce, 24);

    let key = MasterKey::generate();
    let nonce = Nonce::generate();

    #[cfg(feature = "encoding-hex")]
    {
        let hex = key.into_hex();
        println!("key hex: {}", hex.expose_secret());
    }
}

§Opt-In Cloning

Cloning is not implemented by default. It is enabled only for types that implement CloneableSecret (requires the zeroize feature).

#[cfg(feature = "zeroize")]
{
    use secure_gate::{CloneableSecret, Fixed};

    let key1: Fixed<[u8; 32]> = Fixed::new([1u8; 32]);
    let key2 = key1.clone();

    #[derive(Clone, zeroize::Zeroize)]
    struct MyKey([u8; 16]);
    impl CloneableSecret for MyKey {}

    let my_key: Fixed<MyKey> = Fixed::new(MyKey([0u8; 16]));
    let copy = my_key.clone();
}

Blanket implementations exist for primitives and fixed-size arrays.

§Randomness

#[cfg(feature = "rand")]
{
    use secure_gate::fixed_alias_rng;

    fixed_alias_rng!(pub JwtSigningKey, 32);
    fixed_alias_rng!(pub BackupCode, 16);

    let key = JwtSigningKey::generate();
    let code = BackupCode::generate();

    #[cfg(feature = "encoding-hex")]
    {
        let hex_code = code.into_hex();
        println!("Backup code: {}", hex_code.expose_secret());
    }
}

FixedRng<N> can only be constructed via cryptographically secure RNG.

Direct generation is also available:

#[cfg(feature = "rand")]
{
    use secure_gate::{Fixed, Dynamic};

    let key: Fixed<[u8; 32]> = Fixed::generate_random();
    let random: Dynamic<Vec<u8>> = Dynamic::generate_random(64);
}

§Encoding

#[cfg(feature = "encoding-hex")]
{
    use secure_gate::{encoding::hex::HexString, encoding::SecureEncodingExt};

    let bytes = [0u8; 16];
    let hex: String = bytes.to_hex();
    let hex_upper: String = bytes.to_hex_upper();

    let validated = HexString::new("deadbeef".to_string()).unwrap();
    let decoded = validated.to_bytes();
}

#[cfg(feature = "encoding-base64")]
{
    use secure_gate::encoding::base64::Base64String;

    let validated = Base64String::new("SGVsbG8".to_string()).unwrap();
    let decoded = validated.to_bytes();
}

Encoding functions require explicit .expose_secret(). Invalid inputs to HexString::new and Base64String::new are zeroed when the zeroize feature is enabled.

§Constant-Time Equality

#[cfg(feature = "ct-eq")]
{
    use secure_gate::Fixed;

    let a = Fixed::<[u8; 32]>::generate_random();
    let b = Fixed::<[u8; 32]>::generate_random();
    assert!(a.ct_eq(&a));
}

Available on Fixed<[u8; N]> and Dynamic<T> where T: AsRef<[u8]>.

§Macros

use secure_gate::{fixed_alias, dynamic_alias};

fixed_alias!(pub Aes256Key, 32);
dynamic_alias!(pub Password, String);

#[cfg(feature = "rand")]
{
    use secure_gate::fixed_alias_rng;
    fixed_alias_rng!(pub MasterKey, 32);
}

§Memory Guarantees (zeroize enabled)

TypeAllocationAuto-zeroFull wipeSlack eliminatedNotes
Fixed<T>StackYesYesYes (no heap)
Dynamic<T>HeapYesYesNo (until drop)Use shrink_to_fit()
FixedRng<N>StackYesYesYes
HexStringHeapYes (invalid input)YesNo (until drop)Validated hex
Base64StringHeapYes (invalid input)YesNo (until drop)Validated base64

§Performance

The wrappers add no runtime overhead compared to raw types in benchmarks.

§Changelog

CHANGELOG.md

§License

MIT OR Apache-2.0

Re-exports§

pub use clone::CloneableSecret;
pub use random::DynamicRng;
pub use random::FixedRng;

Modules§

clone
Cloning utilities for secrets.
encoding
eq
random

Macros§

dynamic_alias
Creates a type alias for a heap-allocated secure secret.
dynamic_generic_alias
Creates a generic heap-allocated secure secret type alias.
fixed_alias
Creates a type alias for a fixed-size secure secret.
fixed_alias_rng
Creates a type alias for a random-only fixed-size secret.
fixed_generic_alias
Creates a generic (const-sized) fixed secure buffer type.

Structs§

Dynamic
Heap-allocated secure secret wrapper.
Fixed
Stack-allocated secure secret wrapper.
FromSliceError
Error for slice length mismatches in TryFrom impls.