binascii/
lib.rs

1#![no_std]
2#![forbid(unsafe_code)]
3
4//! This crate contains encoders & decoders for various formats (base16, base32 & base64)
5//!
6//! Most functions of this crate work the same way.
7//!
8//! # Quick Example
9//! ```
10//! use binascii::b32decode;
11//!
12//! let mut output_buffer = [0u8; 200];
13//! let message = "MJUW4YLTMNUWSLLSOMQGS4ZAORUGKIDCMVZXIII=";
14//!
15//! let result = b32decode(&message.as_bytes(), &mut output_buffer).ok().unwrap();
16//!
17//! assert_eq!(result, "binascii-rs is the best!".as_bytes());
18//! ```
19
20#[cfg(test)]
21mod tests;
22
23/// Enum that identifies possible failure in encoding binary or decoding text
24#[derive(Debug, PartialEq)]
25pub enum ConvertError {
26    /// This error means that the `input` buffer's length is too short or not right (padding)
27    InvalidInputLength,
28
29    /// The given `output` is too short
30    InvalidOutputLength,
31
32    /// Failure to decode due to malformed input
33    InvalidInput,
34}
35
36/// **Base16 Decoder** - Converts a hexadecimal string to it's binary form.
37///
38/// # Example
39///
40/// ```
41/// use binascii::hex2bin;
42///
43/// let mut my_output_buffer = [0u8; 200];
44///
45/// // If `hex2bin` succeedes, the result will be a `slice` of `my_output_buffer` containing the decoded data.
46/// let res = hex2bin("48656C6C6F2C20576F726C6421".as_bytes(), &mut my_output_buffer);
47///
48/// assert_eq!(res.ok().unwrap(), "Hello, World!".as_bytes());
49/// ```
50///
51/// # Failures
52/// This function will fail with:
53/// - `ConvertError::InvalidInputLength` - If the `input` slice's length is an odd number.
54/// - `ConvertError::InvalidOutputLength` - If the `output`'s length isn't at least half of `input`'s length.
55/// - `ConvertError::InvalidInput` - If the `input` contains characters that are not valid hex digits.
56#[cfg(feature = "decode")]
57pub fn hex2bin<'a>(input: &[u8], output: &'a mut [u8]) -> Result<&'a mut [u8], ConvertError> {
58    if input.len() % 2 != 0 {
59        return Err(ConvertError::InvalidInputLength);
60    }
61
62    if input.len() / 2 > output.len() {
63        return Err(ConvertError::InvalidOutputLength);
64    }
65
66    for block_num in 0..(input.len() / 2) {
67        let mut num = 0u8;
68        for &digit in &input[(block_num * 2)..(block_num * 2 + 2)] {
69            let val = match digit {
70                b'a'..=b'f' => digit - b'a' + 10,
71                b'A'..=b'F' => digit - b'A' + 10,
72                b'0'..=b'9' => digit - b'0',
73                _ => return Err(ConvertError::InvalidInput),
74            };
75
76            num = (num << 4) | val;
77        }
78
79        output[block_num] = num;
80    }
81
82    Ok(&mut output[..(input.len() / 2)])
83}
84
85/// **Base16 Encoder** - Converts binary to base16 (hex)
86///
87/// # Example
88///
89/// ```
90/// use binascii::bin2hex;
91///
92/// let mut buffer = [0u8; 200];
93/// let input = "Hello, World!";
94/// println!("hex({}) = {:?}", input, bin2hex(input.as_bytes(), &mut buffer).ok().unwrap());
95/// ```
96///
97/// # Failures
98/// This function will fail with:
99/// - `ConvertError::InvalidOutputLength` - If the `output`'s length isn't at least 2 times the `input` length.
100#[cfg(feature = "encode")]
101pub fn bin2hex<'a>(input: &[u8], output: &'a mut [u8]) -> Result<&'a mut [u8], ConvertError> {
102    const DIGITS: &[u8] = &[
103        b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a' , b'b',
104        b'c', b'd', b'e', b'f'
105    ];
106
107    if output.len() < input.len() * 2 {
108        return Err(ConvertError::InvalidOutputLength);
109    }
110
111    for (idx, &byte) in input.iter().enumerate() {
112        output[idx * 2 + 0] = DIGITS[((byte >> 4) & 0x0f) as usize];
113        output[idx * 2 + 1] = DIGITS[((byte >> 0) & 0x0f) as usize];
114    }
115
116    Ok(&mut output[..(input.len() * 2)])
117}
118
119/// **Base32 Encoder** - Convert arbitrary data to a base32 string
120///
121/// # Failures
122/// This function will fail with `Err(ConvertError::InvalidOutputLength)` if `output`'s length isn't least `input.len()` * 8/5.
123#[cfg(feature = "encode")]
124pub fn b32encode<'a>(input: &[u8], output: &'a mut [u8]) -> Result<&'a mut [u8], ConvertError> {
125    const DIGITS: &[u8] = &[
126        b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L',
127        b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X',
128        b'Y', b'Z', b'2', b'3', b'4', b'5', b'6', b'7'
129    ];
130
131    let data_len = input.len() * 8 / 5;
132    let pad_len = 8 - (data_len % 8);
133    let total_len = data_len + if pad_len == 8 { 0 } else { pad_len };
134
135    if total_len == 0 {
136        return Ok(&mut output[0..0]);
137    }
138
139    if total_len > output.len() {
140        return Err(ConvertError::InvalidOutputLength);
141    }
142
143    for block_idx in 0..(1 + input.len() / 5) {
144        let max_block_len = if input.len() > block_idx * 5 + 5 { block_idx * 5 + 5 } else { input.len() };
145        let block = &input[block_idx * 5..max_block_len];
146
147        let mut num = 0u64;
148        for i in 0..block.len() {
149            num |= (block[i] as u64) << (64 - 8 - i*8);
150        }
151
152        for i in 0..8 {
153            let digit_idx = (num >> (64 - 5 - i*5)) & 0b11111;
154            output[block_idx * 8 + i] = DIGITS[digit_idx as usize];
155        }
156    }
157
158    for idx in data_len + 1..total_len {
159        output[idx] = b'=';
160    }
161
162    Ok(&mut output[..total_len])
163}
164
165/// **Base32 Decoder** - Converts a base32 encoded string to it's raw form
166///
167/// # Failures
168/// This method will fail with:
169/// - `ConvertError::InvalidOutputLength` if `output`'s length isn't at least `input.len()` * 5/8.
170/// - `ConvertError::InvalidInput` if the input contains invalid characters.
171#[cfg(feature = "decode")]
172pub fn b32decode<'a>(input: &[u8], output: &'a mut [u8]) -> Result<&'a mut [u8], ConvertError> {
173    let padding = 8 - input.len() % 8;
174    let input_len = input.len() + if padding != 8 { padding } else { 0 };
175
176    let mut output_len = input_len * 5 / 8;
177    if output_len > output.len() {
178        return Err(ConvertError::InvalidOutputLength);
179    }
180
181    let mut eof = false;
182
183    for block_idx in 0..(1 + input.len() / 8) {
184        let block_end = if input.len() > block_idx * 8 + 8 { block_idx * 8 + 8 } else { input.len() };
185        let block = &input[(block_idx * 8)..block_end];
186
187        let mut num = 0u64;
188        for idx in 0..block.len() {
189            let ch = match block[idx] {
190                b'=' => { eof = true; continue },
191                // this should have been padding...
192                _ if eof => return Err(ConvertError::InvalidInput),
193                b'1' => b'I',
194                b'0' => b'O',
195                c => c,
196            };
197
198            let c_val = match ch {
199                b'A'..=b'Z' => ch - b'A',
200                b'a'..=b'z' => ch - b'a',
201                b'2'..=b'7' => ch - b'2' + 26,
202                _ => return Err(ConvertError::InvalidInput)
203            };
204
205            num |= (c_val as u64) << (64 - 5 - idx * 5);
206            output_len = block_idx * 5 + (idx * 5 / 8) + 1;
207        }
208
209        if block_idx * 5 + 5 > output.len() {
210            return Err(ConvertError::InvalidOutputLength);
211        }
212
213        for i in 0..5 {
214            output[block_idx * 5 + i] = ((num >> (64 - 8 - i * 8)) & 0xff) as u8;
215        }
216    }
217
218    Ok(&mut output[..output_len])
219}
220
221/// **Base64 Encoder** - Converts data to a base64 encoded string.
222///
223/// # Failures
224/// This function will return `Err(ConvertError::InvalidOutputLength)` if `output`'s length isn't at least `input.len()` * 4 /3.
225#[cfg(feature = "encode")]
226pub fn b64encode<'a>(input: &[u8], output: &'a mut [u8]) -> Result<&'a mut [u8], ConvertError> {
227    const DIGITS: &[u8] = &[
228        b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L',
229        b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X',
230        b'Y', b'Z', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j',
231        b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v',
232        b'w', b'x', b'y', b'z', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7',
233        b'8', b'9', b'+', b'/'
234    ];
235
236    let data_len = input.len() * 4 / 3;
237    let pad_len = (4 - (data_len % 4)) % 4;
238    let required_len = data_len + pad_len;
239    if required_len > output.len() {
240        return Err(ConvertError::InvalidOutputLength);
241    }
242
243    for block_idx in 0..(input.len() / 3 + 1) {
244        let block_end = core::cmp::min(block_idx * 3 + 3, input.len());
245        let block = &input[block_idx * 3..block_end];
246        if block.len() == 0 {
247            break;
248        }
249
250        // convert block to a u32
251        let mut raw_num = 0u32;
252        for i in 0..block.len() {
253            raw_num |= (block[i] as u32) << (16 - (i * 8));
254        }
255
256        for i in 0..4 {
257            let di = (raw_num >> (18 - (6 * i))) & 0b111111;
258            output[block_idx * 4 + i] = DIGITS[di as usize];
259        }
260    }
261
262    for ch in &mut output[(data_len + 1)..] {
263        *ch = b'=';
264    }
265
266    Ok(&mut output[..required_len])
267}
268
269/// **Base64 Decoder** - Converts a base64 encoded string to it's binary form.
270///
271/// # Failures
272/// This function will fail with:
273/// - `ConvertError::InvalidInputLength` - If the input length isn't divisable by 4 (bad padding)
274/// - `ConvertError::InvalidOutputLength` - If `output`'s length isn't at least 3/4s of `input`'s length
275/// - `ConvertError::InvalidInput` - If an invalid character was encountered while decoding
276#[cfg(feature = "decode")]
277pub fn b64decode<'a>(input: &[u8], output: &'a mut [u8]) -> Result<&'a mut [u8], ConvertError> {
278    if input.len() % 4 != 0 {
279        return Err(ConvertError::InvalidInputLength);
280    }
281
282    let mut output_length = input.len() / 4 * 3;
283    if output_length > output.len() {
284        return Err(ConvertError::InvalidOutputLength);
285    }
286
287    for block_idx in 0..(input.len() / 4) {
288        let block = &input[block_idx * 4..(block_idx * 4 + 4)];
289
290        let mut num = 0u32;
291        for i in 0..4 {
292            let ch = block[i];
293            if ch == b'=' {
294                if i < 2 {
295                    return Err(ConvertError::InvalidInput);
296                }
297
298                // Confirm that the padding bits actually contain zeros, or reject the input
299                if i == 2 {
300                    // This is RFC section 4.2: we should have XY==
301                    // and the 12 bits represented by XY should end in 4 zeros, so that
302                    // there are exactly 8 bits of payload
303                    if block[3] != b'=' { return Err(ConvertError::InvalidInput); }
304                    if num & 0x00ffffff != 0 { return Err(ConvertError::InvalidInput); }
305                } else if i == 3 {
306                    // This is RFC section 4.3: we should have XYZ=
307                    // and the 18 bits represented by XYZ should end in 2 zeros, so that
308                    // there are exactly 16 bits of payload
309                    if num & 0x0000ffff != 0 { return Err(ConvertError::InvalidInput); }
310                }
311
312                output_length = block_idx * 3 + i - 1;
313                break;
314            }
315
316            let c_val = match ch {
317                b'A'..=b'Z' => ch - b'A',
318                b'a'..=b'z' => ch - b'a' + 26,
319                b'0'..=b'9' => ch - b'0' + 52,
320                b'+' => 62,
321                b'/' => 63,
322                _ => return Err(ConvertError::InvalidInput),
323            };
324
325            num |= (c_val as u32) << (26 - 6 * i);
326        }
327
328        for i in 0..3 {
329            output[block_idx * 3 + i] = ((num >> (24 - i * 8)) & 0xff) as u8;
330        }
331    }
332
333    Ok(&mut output[..output_length])
334}