secure_gate/encoding/extensions/
bech32.rs

1#[cfg(feature = "encoding-bech32")]
2#[allow(unused_imports)] // Clippy bug: doesn't recognize use in generics
3use ::bech32::{decode, encode, Hrp};
4
5#[cfg(feature = "rand")]
6use crate::Bech32EncodingError;
7
8// ========================================
9// Consuming (into_) methods on RNG types
10// ========================================
11
12#[cfg(feature = "rand")]
13impl crate::DynamicRandom {
14    /// Consume self and return the random bytes as a validated Bech32 string with the specified HRP.
15    ///
16    /// The raw bytes are zeroized immediately after encoding (via drop of `self`).
17    ///
18    /// # Errors
19    ///
20    /// Returns an error if the HRP is invalid.
21    pub fn try_into_bech32(
22        self,
23        hrp: &str,
24    ) -> Result<crate::encoding::bech32::Bech32String, Bech32EncodingError> {
25        let hrp = Hrp::parse(hrp).map_err(|_| Bech32EncodingError::InvalidHrp)?;
26        let encoded = encode::<::bech32::Bech32>(hrp, self.expose_secret())
27            .map_err(|_| Bech32EncodingError::EncodingFailed)?;
28        Ok(crate::encoding::bech32::Bech32String::new_unchecked(
29            encoded,
30            crate::encoding::bech32::EncodingVariant::Bech32,
31        ))
32    }
33
34    /// Consume self and return the random bytes as a validated Bech32m string with the specified HRP.
35    ///
36    /// The raw bytes are zeroized immediately after encoding (via drop of `self`).
37    ///
38    /// # Errors
39    ///
40    /// Returns an error if the HRP is invalid.
41    pub fn try_into_bech32m(
42        self,
43        hrp: &str,
44    ) -> Result<crate::encoding::bech32::Bech32String, Bech32EncodingError> {
45        let hrp = Hrp::parse(hrp).map_err(|_| Bech32EncodingError::InvalidHrp)?;
46        let encoded = encode::<::bech32::Bech32m>(hrp, self.expose_secret())
47            .map_err(|_| Bech32EncodingError::EncodingFailed)?;
48        Ok(crate::encoding::bech32::Bech32String::new_unchecked(
49            encoded,
50            crate::encoding::bech32::EncodingVariant::Bech32m,
51        ))
52    }
53}
54
55#[cfg(feature = "rand")]
56impl<const N: usize> crate::FixedRandom<N> {
57    /// Consume self and return the random bytes as a validated Bech32 string with the specified HRP.
58    ///
59    /// The raw bytes are zeroized immediately after encoding (via drop of `self`).
60    ///
61    /// # Errors
62    ///
63    /// Returns an error if the HRP is invalid.
64    pub fn try_into_bech32(
65        self,
66        hrp: &str,
67    ) -> Result<crate::encoding::bech32::Bech32String, Bech32EncodingError> {
68        let hrp = Hrp::parse(hrp).map_err(|_| Bech32EncodingError::InvalidHrp)?;
69        let encoded = encode::<::bech32::Bech32>(hrp, self.expose_secret())
70            .map_err(|_| Bech32EncodingError::EncodingFailed)?;
71        Ok(crate::encoding::bech32::Bech32String::new_unchecked(
72            encoded,
73            crate::encoding::bech32::EncodingVariant::Bech32,
74        ))
75    }
76
77    /// Consume self and return the random bytes as a validated Bech32m string with the specified HRP.
78    ///
79    /// The raw bytes are zeroized immediately after encoding (via drop of `self`).
80    ///
81    /// # Errors
82    ///
83    /// Returns an error if the HRP is invalid.
84    pub fn try_into_bech32m(
85        self,
86        hrp: &str,
87    ) -> Result<crate::encoding::bech32::Bech32String, Bech32EncodingError> {
88        let hrp = Hrp::parse(hrp).map_err(|_| Bech32EncodingError::InvalidHrp)?;
89        let encoded = encode::<::bech32::Bech32m>(hrp, self.expose_secret())
90            .map_err(|_| Bech32EncodingError::EncodingFailed)?;
91        Ok(crate::encoding::bech32::Bech32String::new_unchecked(
92            encoded,
93            crate::encoding::bech32::EncodingVariant::Bech32m,
94        ))
95    }
96}
97
98// ========================================
99// Borrowing (to_) methods on RNG types
100// ========================================
101
102#[cfg(feature = "rand")]
103impl<const N: usize> crate::FixedRandom<N> {
104    /// Borrow and encode the random bytes as a validated Bech32 string with the specified HRP (allocates).
105    ///
106    /// The original secret remains intact and usable.
107    ///
108    /// # Errors
109    ///
110    /// Returns an error if the HRP is invalid.
111    pub fn try_to_bech32(
112        &self,
113        hrp: &str,
114    ) -> Result<crate::encoding::bech32::Bech32String, Bech32EncodingError> {
115        let hrp = Hrp::parse(hrp).map_err(|_| Bech32EncodingError::InvalidHrp)?;
116        let encoded = encode::<::bech32::Bech32>(hrp, self.expose_secret())
117            .map_err(|_| Bech32EncodingError::EncodingFailed)?;
118        Ok(crate::encoding::bech32::Bech32String::new_unchecked(
119            encoded,
120            crate::encoding::bech32::EncodingVariant::Bech32,
121        ))
122    }
123
124    /// Borrow and encode the random bytes as a validated Bech32m string with the specified HRP (allocates).
125    ///
126    /// The original secret remains intact and usable.
127    ///
128    /// # Errors
129    ///
130    /// Returns an error if the HRP is invalid.
131    pub fn try_to_bech32m(
132        &self,
133        hrp: &str,
134    ) -> Result<crate::encoding::bech32::Bech32String, Bech32EncodingError> {
135        let hrp = Hrp::parse(hrp).map_err(|_| Bech32EncodingError::InvalidHrp)?;
136        let encoded = encode::<::bech32::Bech32m>(hrp, self.expose_secret())
137            .map_err(|_| Bech32EncodingError::EncodingFailed)?;
138        Ok(crate::encoding::bech32::Bech32String::new_unchecked(
139            encoded,
140            crate::encoding::bech32::EncodingVariant::Bech32m,
141        ))
142    }
143}
144
145#[cfg(feature = "rand")]
146impl crate::DynamicRandom {
147    /// Borrow and encode the random bytes as a validated Bech32 string with the specified HRP (allocates).
148    ///
149    /// The original secret remains intact and usable.
150    ///
151    /// # Errors
152    ///
153    /// Returns an error if the HRP is invalid.
154    pub fn try_to_bech32(
155        &self,
156        hrp: &str,
157    ) -> Result<crate::encoding::bech32::Bech32String, Bech32EncodingError> {
158        let hrp = Hrp::parse(hrp).map_err(|_| Bech32EncodingError::InvalidHrp)?;
159        let encoded = encode::<::bech32::Bech32>(hrp, self.expose_secret())
160            .map_err(|_| Bech32EncodingError::EncodingFailed)?;
161        Ok(crate::encoding::bech32::Bech32String::new_unchecked(
162            encoded,
163            crate::encoding::bech32::EncodingVariant::Bech32,
164        ))
165    }
166
167    /// Borrow and encode the random bytes as a validated Bech32m string with the specified HRP (allocates).
168    ///
169    /// The original secret remains intact and usable.
170    ///
171    /// # Errors
172    ///
173    /// Returns an error if the HRP is invalid.
174    pub fn try_to_bech32m(
175        &self,
176        hrp: &str,
177    ) -> Result<crate::encoding::bech32::Bech32String, Bech32EncodingError> {
178        let hrp = Hrp::parse(hrp).map_err(|_| Bech32EncodingError::InvalidHrp)?;
179        let encoded = encode::<::bech32::Bech32m>(hrp, self.expose_secret())
180            .map_err(|_| Bech32EncodingError::EncodingFailed)?;
181        Ok(crate::encoding::bech32::Bech32String::new_unchecked(
182            encoded,
183            crate::encoding::bech32::EncodingVariant::Bech32m,
184        ))
185    }
186}
187
188// ========================================
189// View types and their implementations
190// ========================================
191
192/// View struct for exposed Bech32 strings, allowing decoding without direct access.
193pub struct Bech32StringView<'a>(pub(crate) &'a String);
194
195impl<'a> Bech32StringView<'a> {
196    /// Decode the validated Bech32/Bech32m string into raw bytes (allocates).
197    pub fn to_bytes(&self) -> Vec<u8> {
198        let (_, data) = decode(self.0.as_str()).expect("Bech32String is always valid");
199        data
200    }
201}
202
203impl<'a> core::ops::Deref for Bech32StringView<'a> {
204    type Target = str;
205    fn deref(&self) -> &Self::Target {
206        self.0.as_str()
207    }
208}
209
210impl<'a> core::fmt::Debug for Bech32StringView<'a> {
211    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
212        f.write_str("[REDACTED]")
213    }
214}
215
216impl<'a> core::fmt::Display for Bech32StringView<'a> {
217    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
218        f.write_str(self.0)
219    }
220}
221
222impl<'a> core::cmp::PartialEq<&str> for Bech32StringView<'a> {
223    fn eq(&self, other: &&str) -> bool {
224        self.0 == *other
225    }
226}
227
228// ========================================
229// expose_secret → view implementations
230// ========================================
231
232impl crate::encoding::bech32::Bech32String {
233    pub fn expose_secret(&self) -> Bech32StringView<'_> {
234        Bech32StringView(self.inner.expose_secret())
235    }
236}