secure_gate/traits/encoding/bech32.rs
1#[cfg(feature = "encoding-bech32")]
2use super::super::helpers::bech32::{encode_lower, Bech32Large, Hrp};
3#[cfg(feature = "encoding-bech32")]
4use crate::error::Bech32Error; // With checksum, large CODE_LENGTH
5
6//
7// Extension trait for encoding byte data to Bech32 strings with a specified Human-Readable Part (HRP).
8//
9// This trait provides secure, explicit encoding of byte slices to Bech32 strings using BIP-173 checksum.
10// All methods require the caller to first call `.expose_secret()` (or similar).
11//
12// ## Security Warning
13//
14// These methods produce human-readable strings containing the full secret.
15// Use only when intentionally exposing the secret (e.g., QR codes, user export, audited logging).
16// For debugging/logging, prefer redacted helpers like `to_hex_left` from `ToHex`.
17// All calls require explicit `.expose_secret()` first — no implicit paths exist.
18//
19// Decoding input from untrusted sources should use fallible `try_` methods.
20//
21// ## Example
22//
23// ```rust
24// use secure_gate::traits::ToBech32;
25// let bytes = [0x42u8; 20];
26// let bech32_string = bytes.to_bech32("bc");
27// // bech32_string is now a Bech32 encoded String with "bc" HRP
28// ```
29#[cfg(feature = "encoding-bech32")]
30pub trait ToBech32 {
31 /// Encode secret bytes as Bech32 with the specified HRP.
32 fn to_bech32(&self, hrp: &str) -> alloc::string::String;
33
34 /// Fallibly encode secret bytes as Bech32 with the specified HRP and optional expected HRP validation.
35 fn try_to_bech32(
36 &self,
37 hrp: &str,
38 expected_hrp: Option<&str>,
39 ) -> Result<alloc::string::String, Bech32Error>;
40}
41
42/// Blanket impl to cover any AsRef<[u8]> (e.g., `&[u8]`, `Vec<u8>`, `[u8; N]`, etc.)
43#[cfg(feature = "encoding-bech32")]
44impl<T: AsRef<[u8]> + ?Sized> ToBech32 for T {
45 #[inline(always)]
46 fn to_bech32(&self, hrp: &str) -> alloc::string::String {
47 let hrp_parsed = Hrp::parse(hrp).expect("invalid hrp");
48 encode_lower::<Bech32Large>(hrp_parsed, self.as_ref()).expect("bech32 encoding failed")
49 }
50
51 #[inline(always)]
52 fn try_to_bech32(
53 &self,
54 hrp: &str,
55 expected_hrp: Option<&str>,
56 ) -> Result<alloc::string::String, Bech32Error> {
57 let hrp_parsed = Hrp::parse(hrp).map_err(|_| Bech32Error::InvalidHrp)?;
58 if let Some(exp) = expected_hrp {
59 if hrp != exp {
60 return Err(Bech32Error::UnexpectedHrp {
61 expected: exp.to_string(),
62 got: hrp.to_string(),
63 });
64 }
65 }
66 encode_lower::<Bech32Large>(hrp_parsed, self.as_ref())
67 .map_err(|_| Bech32Error::OperationFailed)
68 }
69}