Skip to main content

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}