ct_codecs/hex.rs
1use crate::error::*;
2use crate::{Decoder, Encoder};
3
4/// Hexadecimal encoder and decoder implementation.
5///
6/// Provides constant-time encoding and decoding of binary data to and from
7/// hexadecimal representation. The implementation uses only lowercase
8/// hexadecimal characters (0-9, a-f) for encoding.
9///
10/// # Security
11///
12/// All operations run in constant time relative to input length, making
13/// this implementation suitable for handling sensitive cryptographic data.
14///
15/// # Examples
16///
17/// ```
18/// use ct_codecs::{Hex, Encoder, Decoder};
19///
20/// fn example() -> Result<(), ct_codecs::Error> {
21/// let data = b"Hello";
22///
23/// // Encode binary data to hex
24/// let encoded = Hex::encode_to_string(data)?;
25/// assert_eq!(encoded, "48656c6c6f");
26///
27/// // Decode hex back to binary
28/// let decoded = Hex::decode_to_vec(&encoded, None)?;
29/// assert_eq!(decoded, data);
30///
31/// // Working with preallocated buffers (useful for no_std)
32/// let mut hex_buf = [0u8; 10];
33/// let hex = Hex::encode(&mut hex_buf, data)?;
34/// assert_eq!(hex, b"48656c6c6f");
35///
36/// let mut bin_buf = [0u8; 5];
37/// let bin = Hex::decode(&mut bin_buf, hex, None)?;
38/// assert_eq!(bin, data);
39/// Ok(())
40/// }
41/// # example().unwrap();
42/// ```
43pub struct Hex;
44
45impl Encoder for Hex {
46 /// Calculates the encoded length for a hexadecimal representation.
47 ///
48 /// The encoded length is always twice the binary length, as each byte
49 /// is represented by two hexadecimal characters.
50 ///
51 /// # Arguments
52 ///
53 /// * `bin_len` - The length of the binary input in bytes
54 ///
55 /// # Returns
56 ///
57 /// * `Ok(usize)` - The required length for the encoded output
58 /// * `Err(Error::Overflow)` - If the calculation would overflow
59 #[inline]
60 fn encoded_len(bin_len: usize) -> Result<usize, Error> {
61 bin_len.checked_mul(2).ok_or(Error::Overflow)
62 }
63
64 /// Encodes binary data into a hexadecimal representation.
65 ///
66 /// The encoding is performed in constant time relative to the input length.
67 ///
68 /// # Arguments
69 ///
70 /// * `hex` - Mutable buffer to store the encoded output
71 /// * `bin` - Binary input data to encode
72 ///
73 /// # Returns
74 ///
75 /// * `Ok(&[u8])` - A slice of the encoded buffer containing the hex data
76 /// * `Err(Error::Overflow)` - If the output buffer is too small
77 fn encode<IN: AsRef<[u8]>>(hex: &mut [u8], bin: IN) -> Result<&[u8], Error> {
78 let bin = bin.as_ref();
79 let bin_len = bin.len();
80 let hex_maxlen = hex.len();
81 if hex_maxlen < bin_len.checked_shl(1).ok_or(Error::Overflow)? {
82 return Err(Error::Overflow);
83 }
84 for (i, v) in bin.iter().enumerate() {
85 let (b, c) = ((v >> 4) as u16, (v & 0xf) as u16);
86 let x = (((87 + c + (((c.wrapping_sub(10)) >> 8) & !38)) as u8) as u16) << 8
87 | ((87 + b + (((b.wrapping_sub(10)) >> 8) & !38)) as u8) as u16;
88 hex[i * 2] = x as u8;
89 hex[i * 2 + 1] = (x >> 8) as u8;
90 }
91 Ok(&hex[..bin_len * 2])
92 }
93}
94
95impl Decoder for Hex {
96 /// Decodes hexadecimal data back into its binary representation.
97 ///
98 /// The decoding is performed in constant time relative to the input length.
99 /// Both uppercase and lowercase hexadecimal characters are accepted.
100 ///
101 /// # Arguments
102 ///
103 /// * `bin` - Mutable buffer to store the decoded output
104 /// * `hex` - Hexadecimal input data to decode
105 /// * `ignore` - Optional set of characters to ignore during decoding
106 ///
107 /// # Returns
108 ///
109 /// * `Ok(&[u8])` - A slice of the binary buffer containing the decoded data
110 /// * `Err(Error::Overflow)` - If the output buffer is too small
111 /// * `Err(Error::InvalidInput)` - If the input contains invalid characters or has odd length
112 fn decode<'t, IN: AsRef<[u8]>>(
113 bin: &'t mut [u8],
114 hex: IN,
115 ignore: Option<&[u8]>,
116 ) -> Result<&'t [u8], Error> {
117 let hex = hex.as_ref();
118 let bin_maxlen = bin.len();
119 let mut bin_pos = 0;
120 let mut state = false;
121 let mut c_acc = 0;
122 for &c in hex {
123 let c_num = c ^ 48;
124 let c_num0 = ((c_num as u16).wrapping_sub(10) >> 8) as u8;
125 let c_alpha = (c & !32).wrapping_sub(55);
126 let c_alpha0 = (((c_alpha as u16).wrapping_sub(10)
127 ^ ((c_alpha as u16).wrapping_sub(16)))
128 >> 8) as u8;
129 if (c_num0 | c_alpha0) == 0 {
130 match ignore {
131 Some(ignore) if ignore.contains(&c) => continue,
132 _ => return Err(Error::InvalidInput),
133 };
134 }
135 let c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha);
136 if bin_pos >= bin_maxlen {
137 return Err(Error::Overflow);
138 }
139 if !state {
140 c_acc = c_val << 4;
141 } else {
142 bin[bin_pos] = c_acc | c_val;
143 bin_pos += 1;
144 }
145 state = !state;
146 }
147 if state {
148 return Err(Error::InvalidInput);
149 }
150 Ok(&bin[..bin_pos])
151 }
152}
153
154#[cfg(feature = "std")]
155#[test]
156fn test_hex() {
157 let bin = [1u8, 5, 11, 15, 19, 131];
158 let hex = Hex::encode_to_string(bin).unwrap();
159 let expected = "01050b0f1383";
160 assert_eq!(hex, expected);
161 let bin2 = Hex::decode_to_vec(&hex, None).unwrap();
162 assert_eq!(bin, &bin2[..]);
163}
164
165#[test]
166fn test_hex_no_std() {
167 let bin = [1u8, 5, 11, 15, 19, 131];
168 let expected = "01050b0f1383";
169 let mut hex = [0u8; 12];
170 let hex = Hex::encode_to_str(&mut hex, bin).unwrap();
171 assert_eq!(&hex, &expected);
172 let mut bin2 = [0u8; 6];
173 let bin2 = Hex::decode(&mut bin2, hex, None).unwrap();
174 assert_eq!(bin, bin2);
175}