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