secure_gate/encoding/
mod.rs

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