Skip to main content

secure_gate/traits/decoding/
hex.rs

1//! Hexadecimal decoding trait.
2//!
3//! > **Import path:** `use secure_gate::FromHexStr;`
4//!
5//! This trait provides secure, explicit decoding of hexadecimal strings
6//! to byte vectors. It is designed for handling untrusted input in
7//! cryptographic contexts, such as decoding hex-encoded keys or nonces.
8//!
9//! Requires the `encoding-hex` feature.
10//!
11//! # Security Notes
12//!
13//! - **Treat all input as untrusted**: validate hex strings upstream before wrapping
14//!   in secrets. Invalid hex may indicate tampering or injection attempts.
15//! - **Heap allocation**: Returns `Vec<u8>` — wrap in [`Fixed`](crate::Fixed) or
16//!   [`Dynamic`](crate::Dynamic) to store as a secret.
17//! - **Case-insensitive**: Accepts both uppercase and lowercase hex digits.
18//!
19//! # Example
20//!
21//! ```rust
22//! # #[cfg(feature = "encoding-hex")]
23//! use secure_gate::{FromHexStr, Fixed};
24//! # #[cfg(feature = "encoding-hex")]
25//! {
26//! let bytes = "01234567".try_from_hex().unwrap();
27//! assert_eq!(bytes, vec![0x01, 0x23, 0x45, 0x67]);
28//!
29//! // Wrap result in a secret immediately
30//! let secret: Fixed<[u8; 4]> = Fixed::try_from_hex("deadbeef").unwrap();
31//!
32//! // Error on invalid input
33//! assert!("xyz!".try_from_hex().is_err());
34//! }
35//! ```
36#[cfg(all(feature = "encoding-hex", feature = "alloc"))]
37use crate::error::HexError;
38
39/// Extension trait for decoding hexadecimal strings into byte vectors.
40///
41/// *Requires features `encoding-hex` and `alloc`.*
42///
43/// Blanket-implemented for all `AsRef<str>` types. Returns `Vec<u8>` — requires heap
44/// allocation. For no-alloc targets, use `Fixed::try_from_hex` instead, which decodes
45/// directly into a stack-allocated `[u8; N]` buffer.
46///
47/// **The returned `Vec<u8>` is plain heap memory and is not zeroized on drop.** Wrap
48/// the result in [`Fixed`](crate::Fixed) or [`Dynamic`](crate::Dynamic) immediately
49/// (or in [`zeroize::Zeroizing`]) if the decoded bytes are sensitive. Prefer
50/// `Fixed::try_from_hex` / `Dynamic::try_from_hex`, which perform the wrapping for
51/// you and zeroize their internal temporaries.
52///
53/// Treat all input as untrusted; validate lengths and content upstream before wrapping
54/// decoded bytes in secrets.
55#[cfg(all(feature = "encoding-hex", feature = "alloc"))]
56pub trait FromHexStr {
57    /// Decodes a hexadecimal string into a byte vector.
58    ///
59    /// Case-insensitive; rejects odd-length strings and invalid characters.
60    ///
61    /// # Errors
62    ///
63    /// - [`HexError::InvalidHex`] — non-hex characters or odd-length input.
64    ///
65    /// # Examples
66    ///
67    /// ```rust
68    /// use secure_gate::FromHexStr;
69    ///
70    /// let bytes = "deadbeef".try_from_hex()?;
71    /// assert_eq!(bytes, [0xde, 0xad, 0xbe, 0xef]);
72    ///
73    /// assert!("xyz!".try_from_hex().is_err()); // invalid chars
74    /// assert!("a".try_from_hex().is_err());    // odd length
75    /// # Ok::<(), secure_gate::HexError>(())
76    /// ```
77    fn try_from_hex(&self) -> Result<alloc::vec::Vec<u8>, HexError>;
78}
79
80// Blanket impl to cover any AsRef<str> (e.g., &str, String, etc.)
81// Returns Vec<u8> — alloc required.
82#[cfg(all(feature = "encoding-hex", feature = "alloc"))]
83impl<T: AsRef<str> + ?Sized> FromHexStr for T {
84    fn try_from_hex(&self) -> Result<alloc::vec::Vec<u8>, HexError> {
85        base16ct::mixed::decode_vec(self.as_ref().as_bytes()).map_err(|_| HexError::InvalidHex)
86    }
87}