secure_gate/encoding/
mod.rs

1//! Encoding utilities (gated behind “encoding” features).
2
3// ==========================================================================
4// src/encoding/mod.rs
5// ==========================================================================
6
7// Allow unsafe_code when zeroize is enabled (not needed here, but consistent)
8// but forbid it when none of the encoding features are enabled
9#![cfg_attr(
10    not(any(feature = "encoding-hex", feature = "encoding-base64")),
11    forbid(unsafe_code)
12)]
13
14#[cfg(feature = "encoding-hex")]
15pub mod hex;
16
17#[cfg(feature = "encoding-hex")]
18use ::hex as hex_crate;
19
20#[cfg(feature = "encoding-base64")]
21use ::base64 as base64_crate;
22#[cfg(feature = "encoding-base64")]
23use base64_crate::Engine;
24#[cfg(feature = "encoding-base64")]
25pub mod base64;
26
27#[cfg(feature = "encoding-bech32")]
28pub mod bech32;
29
30/// Extension trait for safe, explicit encoding of secret byte data to strings.
31///
32/// All methods require the caller to first call `.expose_secret()` (or similar).
33/// This makes every secret access loud, grep-able, and auditable.
34///
35/// For Bech32 encoding, use `Bech32String` directly for age keys.
36///
37/// # Example
38///
39/// ```
40/// # #[cfg(feature = "encoding")]
41/// # {
42/// # use secure_gate::{fixed_alias, encoding::SecureEncodingExt};
43/// # fixed_alias!(Aes256Key, 32);
44/// # let key = Aes256Key::from([0x42u8; 32]);
45/// # let hex = key.expose_secret().to_hex();         // → "424242..."
46/// # let b64 = key.expose_secret().to_base64url();   // URL-safe, no padding
47/// # assert_eq!(hex, "4242424242424242424242424242424242424242424242424242424242424242");
48/// # }
49/// ```
50// Trait is available when any encoding feature is enabled
51#[cfg(any(feature = "encoding-hex", feature = "encoding-base64"))]
52pub trait SecureEncodingExt {
53    /// Encode secret bytes as lowercase hexadecimal.
54    #[cfg(feature = "encoding-hex")]
55    fn to_hex(&self) -> alloc::string::String;
56
57    /// Encode secret bytes as uppercase hexadecimal.
58    #[cfg(feature = "encoding-hex")]
59    fn to_hex_upper(&self) -> alloc::string::String;
60
61    /// Encode secret bytes as URL-safe base64 (no padding).
62    #[cfg(feature = "encoding-base64")]
63    fn to_base64url(&self) -> alloc::string::String;
64}
65
66#[cfg(feature = "encoding-hex")]
67impl SecureEncodingExt for [u8] {
68    #[cfg(feature = "encoding-hex")]
69    #[inline(always)]
70    fn to_hex(&self) -> alloc::string::String {
71        hex_crate::encode(self)
72    }
73    #[cfg(feature = "encoding-hex")]
74    #[inline(always)]
75    fn to_hex_upper(&self) -> alloc::string::String {
76        hex_crate::encode_upper(self)
77    }
78    #[cfg(feature = "encoding-base64")]
79    #[inline(always)]
80    fn to_base64url(&self) -> alloc::string::String {
81        use ::base64::engine::general_purpose::URL_SAFE_NO_PAD;
82        URL_SAFE_NO_PAD.encode(self)
83    }
84}
85
86#[cfg(feature = "encoding-hex")]
87impl<const N: usize> SecureEncodingExt for [u8; N] {
88    #[cfg(feature = "encoding-hex")]
89    #[inline(always)]
90    fn to_hex(&self) -> alloc::string::String {
91        hex_crate::encode(self)
92    }
93
94    #[cfg(feature = "encoding-hex")]
95    #[inline(always)]
96    fn to_hex_upper(&self) -> alloc::string::String {
97        hex_crate::encode_upper(self)
98    }
99
100    #[cfg(feature = "encoding-base64")]
101    #[inline(always)]
102    fn to_base64url(&self) -> alloc::string::String {
103        use ::base64::engine::general_purpose::URL_SAFE_NO_PAD;
104        URL_SAFE_NO_PAD.encode(self)
105    }
106}