secure_gate/encoding/extensions/
core.rs

1#[cfg(feature = "encoding-hex")]
2use ::hex as hex_crate;
3
4#[cfg(feature = "encoding-base64")]
5use ::base64 as base64_crate;
6#[cfg(feature = "encoding-base64")]
7use base64_crate::engine::general_purpose::URL_SAFE_NO_PAD;
8#[cfg(feature = "encoding-base64")]
9use base64_crate::Engine;
10
11#[cfg(feature = "encoding-bech32")]
12use ::bech32::{self};
13
14#[cfg(feature = "encoding-bech32")]
15use crate::Bech32EncodingError;
16
17/// Extension trait for safe, explicit encoding of secret byte data to strings.
18///
19/// All methods require the caller to first call `.expose_secret()` (or similar).
20/// This makes every secret access loud, grep-able, and auditable.
21///
22/// For Bech32 encoding, use the trait methods with an HRP.
23///
24/// # Example
25///
26/// ```
27/// # #[cfg(feature = "encoding-hex")]
28/// # {
29/// use secure_gate::SecureEncodingExt;
30/// let bytes = [0x42u8; 32];
31/// let hex_string = bytes.to_hex();
32/// let hex = hex_string.expose_secret(); // → "424242..."
33/// # }
34/// ```
35#[cfg(any(
36    feature = "encoding-hex",
37    feature = "encoding-base64",
38    feature = "encoding-bech32"
39))]
40pub trait SecureEncodingExt {
41    /// Encode secret bytes as lowercase hexadecimal.
42    #[cfg(feature = "encoding-hex")]
43    fn to_hex(&self) -> crate::encoding::hex::HexString;
44
45    /// Encode secret bytes as uppercase hexadecimal.
46    #[cfg(feature = "encoding-hex")]
47    fn to_hex_upper(&self) -> alloc::string::String;
48
49    /// Encode secret bytes as URL-safe base64 (no padding).
50    #[cfg(feature = "encoding-base64")]
51    fn to_base64url(&self) -> crate::encoding::base64::Base64String;
52
53    /// Try to encode secret bytes as Bech32 with the specified HRP.
54    #[cfg(feature = "encoding-bech32")]
55    fn try_to_bech32(
56        &self,
57        hrp: &str,
58    ) -> Result<crate::encoding::bech32::Bech32String, Bech32EncodingError>;
59
60    /// Try to encode secret bytes as Bech32m with the specified HRP.
61    #[cfg(feature = "encoding-bech32")]
62    fn try_to_bech32m(
63        &self,
64        hrp: &str,
65    ) -> Result<crate::encoding::bech32::Bech32String, Bech32EncodingError>;
66}
67
68#[cfg(any(
69    feature = "encoding-hex",
70    feature = "encoding-base64",
71    feature = "encoding-bech32"
72))]
73impl SecureEncodingExt for [u8] {
74    #[cfg(feature = "encoding-hex")]
75    #[inline(always)]
76    fn to_hex(&self) -> crate::encoding::hex::HexString {
77        crate::encoding::hex::HexString::new_unchecked(hex_crate::encode(self))
78    }
79
80    #[cfg(feature = "encoding-hex")]
81    #[inline(always)]
82    fn to_hex_upper(&self) -> alloc::string::String {
83        hex_crate::encode_upper(self)
84    }
85
86    #[cfg(feature = "encoding-base64")]
87    #[inline(always)]
88    fn to_base64url(&self) -> crate::encoding::base64::Base64String {
89        crate::encoding::base64::Base64String::new_unchecked(URL_SAFE_NO_PAD.encode(self))
90    }
91
92    #[cfg(feature = "encoding-bech32")]
93    #[inline(always)]
94    fn try_to_bech32(
95        &self,
96        hrp: &str,
97    ) -> Result<crate::encoding::bech32::Bech32String, Bech32EncodingError> {
98        let hrp = bech32::Hrp::parse(hrp).map_err(|_| Bech32EncodingError::InvalidHrp)?;
99        let encoded = bech32::encode::<bech32::Bech32>(hrp, self)
100            .map_err(|_| Bech32EncodingError::EncodingFailed)?;
101        Ok(crate::encoding::bech32::Bech32String::new_unchecked(
102            encoded,
103            crate::encoding::bech32::EncodingVariant::Bech32,
104        ))
105    }
106
107    #[cfg(feature = "encoding-bech32")]
108    #[inline(always)]
109    fn try_to_bech32m(
110        &self,
111        hrp: &str,
112    ) -> Result<crate::encoding::bech32::Bech32String, Bech32EncodingError> {
113        let hrp = bech32::Hrp::parse(hrp).map_err(|_| Bech32EncodingError::InvalidHrp)?;
114        let encoded = bech32::encode::<bech32::Bech32m>(hrp, self)
115            .map_err(|_| Bech32EncodingError::EncodingFailed)?;
116        Ok(crate::encoding::bech32::Bech32String::new_unchecked(
117            encoded,
118            crate::encoding::bech32::EncodingVariant::Bech32m,
119        ))
120    }
121}
122
123#[cfg(any(
124    feature = "encoding-hex",
125    feature = "encoding-base64",
126    feature = "encoding-bech32"
127))]
128impl<const N: usize> SecureEncodingExt for [u8; N] {
129    #[cfg(feature = "encoding-hex")]
130    #[inline(always)]
131    fn to_hex(&self) -> crate::encoding::hex::HexString {
132        crate::encoding::hex::HexString::new_unchecked(hex_crate::encode(self))
133    }
134
135    #[cfg(feature = "encoding-hex")]
136    #[inline(always)]
137    fn to_hex_upper(&self) -> alloc::string::String {
138        hex_crate::encode_upper(self)
139    }
140
141    #[cfg(feature = "encoding-base64")]
142    #[inline(always)]
143    fn to_base64url(&self) -> crate::encoding::base64::Base64String {
144        crate::encoding::base64::Base64String::new_unchecked(URL_SAFE_NO_PAD.encode(self))
145    }
146
147    #[cfg(feature = "encoding-bech32")]
148    #[inline(always)]
149    fn try_to_bech32(
150        &self,
151        hrp: &str,
152    ) -> Result<crate::encoding::bech32::Bech32String, Bech32EncodingError> {
153        let hrp = bech32::Hrp::parse(hrp).map_err(|_| Bech32EncodingError::InvalidHrp)?;
154        let encoded = bech32::encode::<bech32::Bech32>(hrp, self)
155            .map_err(|_| Bech32EncodingError::EncodingFailed)?;
156        Ok(crate::encoding::bech32::Bech32String::new_unchecked(
157            encoded,
158            crate::encoding::bech32::EncodingVariant::Bech32,
159        ))
160    }
161
162    #[cfg(feature = "encoding-bech32")]
163    #[inline(always)]
164    fn try_to_bech32m(
165        &self,
166        hrp: &str,
167    ) -> Result<crate::encoding::bech32::Bech32String, Bech32EncodingError> {
168        let hrp = bech32::Hrp::parse(hrp).map_err(|_| Bech32EncodingError::InvalidHrp)?;
169        let encoded = bech32::encode::<bech32::Bech32m>(hrp, self)
170            .map_err(|_| Bech32EncodingError::EncodingFailed)?;
171        Ok(crate::encoding::bech32::Bech32String::new_unchecked(
172            encoded,
173            crate::encoding::bech32::EncodingVariant::Bech32m,
174        ))
175    }
176}