ct_codecs/
lib.rs

1//! # CT-Codecs
2//!
3//! A Rust implementation of constant-time Base64 and Hexadecimal codecs,
4//! reimplemented from libsodium and libhydrogen.
5//!
6//! ## Features
7//!
8//! - **Constant-time implementation** for cryptographic applications where timing attacks are a concern
9//! - **Strict validation** ensuring Base64 strings are not malleable
10//! - **Multiple variants** of Base64: standard, URL-safe, with and without padding
11//! - **Character filtering** for ignoring specific characters during decoding (like whitespace)
12//! - **Zero dependencies** and **`no_std` compatible**
13//! - **Memory safety** with `#![forbid(unsafe_code)]`
14//!
15//! ## Usage Examples
16//!
17//! ### Base64 Encoding
18//!
19//! ```
20//! use ct_codecs::{Base64, Encoder};
21//!
22//! let data = b"Hello, world!";
23//! # let result = 
24//! let encoded = Base64::encode_to_string(data)?;
25//! assert_eq!(encoded, "SGVsbG8sIHdvcmxkIQ==");
26//! # Ok::<(), ct_codecs::Error>(())
27//! ```
28//!
29//! ### Base64 Decoding
30//!
31//! ```
32//! use ct_codecs::{Base64, Decoder};
33//!
34//! let encoded = "SGVsbG8sIHdvcmxkIQ==";
35//! # let result = 
36//! let decoded = Base64::decode_to_vec(encoded, None)?;
37//! assert_eq!(decoded, b"Hello, world!");
38//! # Ok::<(), ct_codecs::Error>(())
39//! ```
40//!
41//! ### Hexadecimal Encoding/Decoding
42//!
43//! ```
44//! use ct_codecs::{Hex, Encoder, Decoder};
45//!
46//! let data = b"Hello, world!";
47//! # let result = 
48//! let encoded = Hex::encode_to_string(data)?;
49//! let decoded = Hex::decode_to_vec(&encoded, None)?;
50//! assert_eq!(decoded, data);
51//! # Ok::<(), ct_codecs::Error>(())
52//! ```
53//!
54//! ### No-std Usage with Pre-allocated Buffers
55//!
56//! ```
57//! use ct_codecs::{Base64, Encoder, Decoder};
58//!
59//! let data = b"Hello, world!";
60//! # let result = 
61//! let mut encoded_buf = [0u8; 20]; // Must be large enough
62//! let encoded = Base64::encode(&mut encoded_buf, data)?;
63//!
64//! let mut decoded_buf = [0u8; 13]; // Must be large enough
65//! let decoded = Base64::decode(&mut decoded_buf, encoded, None)?;
66//! assert_eq!(decoded, data);
67//! # Ok::<(), ct_codecs::Error>(())
68//! ```
69
70#![cfg_attr(not(feature = "std"), no_std)]
71#![forbid(unsafe_code)]
72
73mod base64;
74mod error;
75mod hex;
76
77pub use base64::*;
78pub use error::*;
79pub use hex::*;
80
81/// Trait for encoding binary data into text representations.
82///
83/// Implementors of this trait provide constant-time encoding operations
84/// for a specific encoding format (Base64, Hex, etc.).
85pub trait Encoder {
86    /// Calculates the length of the encoded output for a given binary input length.
87    ///
88    /// # Arguments
89    ///
90    /// * `bin_len` - The length of the binary input in bytes
91    ///
92    /// # Returns
93    ///
94    /// * `Ok(usize)` - The required length for the encoded output
95    /// * `Err(Error::Overflow)` - If the calculation would overflow
96    fn encoded_len(bin_len: usize) -> Result<usize, Error>;
97
98    /// Encodes binary data into a text representation.
99    ///
100    /// # Arguments
101    ///
102    /// * `encoded` - Mutable buffer to store the encoded output
103    /// * `bin` - Binary input data to encode
104    ///
105    /// # Returns
106    ///
107    /// * `Ok(&[u8])` - A slice of the encoded buffer containing the encoded data
108    /// * `Err(Error::Overflow)` - If the output buffer is too small
109    fn encode<IN: AsRef<[u8]>>(encoded: &mut [u8], bin: IN) -> Result<&[u8], Error>;
110
111    /// Encodes binary data and returns the result as a string slice.
112    ///
113    /// # Arguments
114    ///
115    /// * `encoded` - Mutable buffer to store the encoded output
116    /// * `bin` - Binary input data to encode
117    ///
118    /// # Returns
119    ///
120    /// * `Ok(&str)` - A string slice containing the encoded data
121    /// * `Err(Error::Overflow)` - If the output buffer is too small
122    fn encode_to_str<IN: AsRef<[u8]>>(encoded: &mut [u8], bin: IN) -> Result<&str, Error> {
123        Ok(core::str::from_utf8(Self::encode(encoded, bin)?).unwrap())
124    }
125
126    /// Encodes binary data and returns the result as a String.
127    ///
128    /// This method is only available when the `std` feature is enabled.
129    ///
130    /// # Arguments
131    ///
132    /// * `bin` - Binary input data to encode
133    ///
134    /// # Returns
135    ///
136    /// * `Ok(String)` - A String containing the encoded data
137    /// * `Err(Error::Overflow)` - If the calculation would overflow
138    #[cfg(feature = "std")]
139    fn encode_to_string<IN: AsRef<[u8]>>(bin: IN) -> Result<String, Error> {
140        let mut encoded = vec![0u8; Self::encoded_len(bin.as_ref().len())?];
141        let encoded_len = Self::encode(&mut encoded, bin)?.len();
142        encoded.truncate(encoded_len);
143        Ok(String::from_utf8(encoded).unwrap())
144    }
145}
146
147/// Trait for decoding text representations back into binary data.
148///
149/// Implementors of this trait provide constant-time decoding operations
150/// for a specific encoding format (Base64, Hex, etc.).
151pub trait Decoder {
152    /// Decodes text data back into its binary representation.
153    ///
154    /// # Arguments
155    ///
156    /// * `bin` - Mutable buffer to store the decoded output
157    /// * `encoded` - Text input data to decode
158    /// * `ignore` - Optional set of characters to ignore during decoding (e.g., whitespace)
159    ///
160    /// # Returns
161    ///
162    /// * `Ok(&[u8])` - A slice of the binary buffer containing the decoded data
163    /// * `Err(Error::Overflow)` - If the output buffer is too small
164    /// * `Err(Error::InvalidInput)` - If the input contains invalid characters
165    fn decode<'t, IN: AsRef<[u8]>>(
166        bin: &'t mut [u8],
167        encoded: IN,
168        ignore: Option<&[u8]>,
169    ) -> Result<&'t [u8], Error>;
170
171    /// Decodes text data and returns the result as a Vec<u8>.
172    ///
173    /// This method is only available when the `std` feature is enabled.
174    ///
175    /// # Arguments
176    ///
177    /// * `encoded` - Text input data to decode
178    /// * `ignore` - Optional set of characters to ignore during decoding (e.g., whitespace)
179    ///
180    /// # Returns
181    ///
182    /// * `Ok(Vec<u8>)` - A Vec containing the decoded binary data
183    /// * `Err(Error::InvalidInput)` - If the input contains invalid characters
184    #[cfg(feature = "std")]
185    fn decode_to_vec<IN: AsRef<[u8]>>(
186        encoded: IN,
187        ignore: Option<&[u8]>,
188    ) -> Result<Vec<u8>, Error> {
189        let mut bin = vec![0u8; encoded.as_ref().len()];
190        let bin_len = Self::decode(&mut bin, encoded, ignore)?.len();
191        bin.truncate(bin_len);
192        Ok(bin)
193    }
194}