secure-gate 0.9.0-rc.5

Secure wrappers for secrets with explicit access and mandatory zeroization — no_std-compatible, zero-overhead library with audit-friendly access patterns.
Documentation
//! Hexadecimal encoding trait.
//!
//! > **Import path:** `use secure_gate::ToHex;`
//!
//! This trait provides secure, explicit encoding of byte data to lowercase
//! (or uppercase) hexadecimal strings. It is intended for intentional export
//! only (QR codes, audited logs, API responses).
//!
//! Requires the `encoding-hex` feature.
//!
//! # Security Notes
//!
//! - **Full secret exposure**: The resulting string contains the **entire** secret.
//!   Always treat output as sensitive; do not log or persist without protection.
//! - **Zeroizing variants**: `to_hex_zeroizing()` / `to_hex_upper_zeroizing()` return
//!   [`EncodedSecret`] (wrapping `Zeroizing<String>` with redacted `Debug`). Prefer these
//!   when the encoded form itself is sensitive.
//! - **Audit visibility**: Direct calls (`key.to_hex()` / `key.to_hex_upper()`) do **not** appear in
//!   `grep expose_secret` / `grep with_secret` audit sweeps. For audit-first teams or
//!   multi-step operations, prefer `with_secret(|b| b.to_hex())` — the borrow checker
//!   enforces the reference cannot escape the closure.
//! - **Treat all input as untrusted**: validate hex strings upstream before wrapping
//!   in secrets.
//!
//! # Example
//!
//! ```rust
//! # #[cfg(feature = "encoding-hex")]
//! use secure_gate::{Fixed, ToHex, RevealSecret};
//! # #[cfg(feature = "encoding-hex")]
//! {
//! let secret = Fixed::new([0x0au8, 0x0bu8, 0x0cu8, 0x0du8]);
//!
//! // Blanket impl on the inner byte array (via with_secret):
//! let hex = secret.with_secret(|s| s.to_hex());
//! assert_eq!(hex, "0a0b0c0d");
//!
//! let hex_upper = secret.with_secret(|s| s.to_hex_upper());
//! assert_eq!(hex_upper, "0A0B0C0D");
//!
//! // Wrapper method (Direct Fixed<[u8; N]> API — same result):
//! assert_eq!(secret.to_hex(), "0a0b0c0d");
//! }
//! ```
#[cfg(all(feature = "encoding-hex", feature = "alloc"))]
use base16ct;

/// Extension trait for encoding byte data as hexadecimal strings.
///
/// *Requires feature `encoding-hex`.*
///
/// Blanket-implemented for all `AsRef<[u8]>` types (byte slices, arrays, `Vec<u8>`).
/// To encode a secret wrapper, call the inherent `to_hex()` method directly (ergonomically
/// safest for single operations — no reference in the caller's hands), or use
/// `with_secret(|b| b.to_hex())` for multi-step operations or when audit-greppability matters.
#[cfg(all(feature = "encoding-hex", feature = "alloc"))]
pub trait ToHex {
    /// Encode bytes as lowercase hexadecimal.
    fn to_hex(&self) -> alloc::string::String;

    /// Encode bytes as uppercase hexadecimal.
    fn to_hex_upper(&self) -> alloc::string::String;

    /// Encode bytes as lowercase hexadecimal and wrap the result in [`crate::EncodedSecret`].
    fn to_hex_zeroizing(&self) -> crate::EncodedSecret;

    /// Encode bytes as uppercase hexadecimal and wrap the result in [`crate::EncodedSecret`].
    fn to_hex_upper_zeroizing(&self) -> crate::EncodedSecret;
}

// Blanket impl to cover any AsRef<[u8]> (e.g., &[u8], Vec<u8>, [u8; N], etc.)
// encode_string requires alloc — the trait itself is alloc-gated.
#[cfg(all(feature = "encoding-hex", feature = "alloc"))]
impl<T: AsRef<[u8]> + ?Sized> ToHex for T {
    #[inline(always)]
    fn to_hex(&self) -> alloc::string::String {
        base16ct::lower::encode_string(self.as_ref())
    }

    #[inline(always)]
    fn to_hex_upper(&self) -> alloc::string::String {
        base16ct::upper::encode_string(self.as_ref())
    }

    #[inline(always)]
    fn to_hex_zeroizing(&self) -> crate::EncodedSecret {
        crate::EncodedSecret::new(self.to_hex())
    }

    #[inline(always)]
    fn to_hex_upper_zeroizing(&self) -> crate::EncodedSecret {
        crate::EncodedSecret::new(self.to_hex_upper())
    }
}