Skip to main content

secure_gate/traits/encoding/
base64_url.rs

1//! URL-safe Base64 encoding trait.
2//!
3//! > **Import path:** `use secure_gate::ToBase64Url;`
4//!
5//! This trait provides secure, explicit encoding of byte data to URL-safe
6//! base64 strings (no padding, RFC 4648). It is intended for intentional
7//! export scenarios only (QR codes, API responses, audited logging).
8//!
9//! Requires the `encoding-base64` feature.
10//!
11//! # Security Notes
12//!
13//! - **Full secret exposure**: The resulting string contains the **entire** secret.
14//!   Always treat output as sensitive; do not log or persist without protection.
15//! - **Zeroizing variants**: Prefer `to_base64url_zeroizing()`, which returns [`EncodedSecret`]
16//!   (wrapping `Zeroizing<String>` with redacted `Debug`). Use plain `to_base64url()`
17//!   only for public values.
18//! - **Explicit exposure**: `to_base64url()` (and the other encoding methods) perform deliberate full-secret exposure —
19//!   the same security contract as `with_secret` or `expose_secret`. Direct calls do not
20//!   appear in `grep expose_secret` / `grep with_secret` audit sweeps. For audit-first teams
21//!   or multi-step operations, prefer `with_secret(|b| b.to_base64url())` — the borrow
22//!   checker enforces the reference cannot escape the closure.
23//! - **URL-safe**: No padding (`=`), safe for URLs/JSON/filenames.
24//!
25//! # Example
26//!
27//! ```rust
28//! # #[cfg(feature = "encoding-base64")]
29//! use secure_gate::{Fixed, ToBase64Url, RevealSecret};
30//! # #[cfg(feature = "encoding-base64")]
31//! {
32//! let secret = Fixed::new([0x42u8; 4]);
33//!
34//! // Blanket impl on the inner byte array (via with_secret):
35//! let b64 = secret.with_secret(|s| s.to_base64url());
36//! assert_eq!(b64, "QkJCQg");
37//!
38//! // Wrapper method (Direct Fixed<[u8; N]> API — same result):
39//! assert_eq!(secret.to_base64url(), "QkJCQg");
40//!
41//! // Zeroizing variant for sensitive encoded output:
42//! let b64z = secret.to_base64url_zeroizing();
43//! // b64z is EncodedSecret — zeroized on drop, redacted Debug
44//! }
45//! ```
46#[cfg(all(feature = "encoding-base64", feature = "alloc"))]
47use base64ct::{Base64UrlUnpadded, Encoding};
48
49/// Extension trait for encoding byte data as URL-safe base64 strings (no padding).
50///
51/// *Requires feature `encoding-base64`.*
52///
53/// Blanket-implemented for all `AsRef<[u8]>` types. Uses the RFC 4648 URL-safe
54/// alphabet without `=` padding. To encode a secret wrapper, call the inherent
55/// `to_base64url()` method directly (ergonomically safest for single operations), or
56/// use `with_secret(|b| b.to_base64url())` for multi-step operations or when
57/// audit-greppability matters.
58#[cfg(all(feature = "encoding-base64", feature = "alloc"))]
59pub trait ToBase64Url {
60    /// Encode bytes as URL-safe base64 (no padding).
61    fn to_base64url(&self) -> alloc::string::String;
62
63    /// Encode bytes as URL-safe base64 and wrap the result in [`crate::EncodedSecret`].
64    fn to_base64url_zeroizing(&self) -> crate::EncodedSecret;
65}
66
67// Blanket impl to cover any AsRef<[u8]> (e.g., &[u8], Vec<u8>, [u8; N], etc.)
68#[cfg(all(feature = "encoding-base64", feature = "alloc"))]
69impl<T: AsRef<[u8]> + ?Sized> ToBase64Url for T {
70    #[inline(always)]
71    fn to_base64url(&self) -> alloc::string::String {
72        Base64UrlUnpadded::encode_string(self.as_ref())
73    }
74
75    #[inline(always)]
76    fn to_base64url_zeroizing(&self) -> crate::EncodedSecret {
77        crate::EncodedSecret::new(self.to_base64url())
78    }
79}