Skip to main content

rust_web_server/core/base64/
mod.rs

1//! Base64 encoding and decoding of byte arrays. [`Decoder`] is the one
2//! decoder in the crate (standard, URL-safe or either alphabet; optional
3//! whitespace); [`Base64`] keeps the original standard-alphabet API.
4
5use std::collections::HashMap;
6use crate::symbol::SYMBOL;
7
8mod decode;
9pub use decode::{Alphabet, DecodeError, Decoder};
10
11#[cfg(test)]
12mod tests;
13
14const STANDARD_CHARS: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
15
16/// Standard, padded base64.
17fn encode_standard(bytes: &[u8]) -> String {
18    let mut out = Vec::with_capacity(bytes.len().div_ceil(3) * 4);
19    let (chunks, remainder) = bytes.as_chunks::<3>();
20    for c in chunks {
21        let n = (c[0] as usize) << 16 | (c[1] as usize) << 8 | c[2] as usize;
22        out.extend_from_slice(&[STANDARD_CHARS[n >> 18], STANDARD_CHARS[n >> 12 & 63], STANDARD_CHARS[n >> 6 & 63], STANDARD_CHARS[n & 63]]);
23    }
24    match *remainder {
25        [a] => {
26            let n = (a as usize) << 16;
27            out.extend_from_slice(&[STANDARD_CHARS[n >> 18], STANDARD_CHARS[n >> 12 & 63], b'=', b'=']);
28        }
29        [a, b] => {
30            let n = (a as usize) << 16 | (b as usize) << 8;
31            out.extend_from_slice(&[STANDARD_CHARS[n >> 18], STANDARD_CHARS[n >> 12 & 63], STANDARD_CHARS[n >> 6 & 63], b'=']);
32        }
33        _ => {}
34    }
35    // Only ASCII went in.
36    String::from_utf8(out).unwrap_or_default()
37}
38
39pub struct Base64;
40
41impl Base64 {
42
43    /// Standard, padded base64 of `bytes` (never fails; the `Result` is
44    /// kept for compatibility).
45    pub fn encode(bytes: &[u8]) -> Result<String, String> {
46        Ok(encode_standard(bytes))
47    }
48
49    /// Decode standard base64 (padding optional, concatenated padded
50    /// segments allowed, no whitespace) — [`Decoder::STANDARD`].
51    pub fn decode(text: String) -> Result<Vec<u8>, String> {
52        Ok(Decoder::STANDARD.decode(text.as_bytes())?)
53    }
54
55    pub fn decode_sequence(text: String) -> Result<Vec<u8>, String> {
56        let result : Vec<u8> = vec![];
57
58        let number_of_equal_signs = text.matches(SYMBOL.equals).count();
59
60        if number_of_equal_signs == 2 {
61            let boxed_first_byte = text.chars().nth(0);
62            if boxed_first_byte.is_none() {
63                return Err("unexpected error, unable to get char at position 0".to_string());
64            }
65            let first_byte = boxed_first_byte.unwrap() as u8;
66            let _first_byte_as_string = format!("{first_byte:b}");
67
68            let boxed_conversion = Base64::convert_base64_char_to_number(first_byte as char);
69            if boxed_conversion.is_err() {
70                let message = boxed_conversion.err().unwrap();
71                return Err(message);
72            }
73            let converted_first_byte = boxed_conversion.unwrap();
74            let shifted_converted_first_byte = converted_first_byte << 2;
75            let _shifted_converted_first_byte_as_string = format!("{converted_first_byte:b}");
76
77
78
79            let boxed_second_byte = text.chars().nth(1);
80            if boxed_second_byte.is_none() {
81                return Err("unexpected error, unable to get char at position 0".to_string());
82            }
83            let second_byte = boxed_second_byte.unwrap() as u8;
84            let _second_byte_as_string = format!("{second_byte:b}");
85
86
87            let boxed_conversion = Base64::convert_base64_char_to_number(second_byte as char);
88            if boxed_conversion.is_err() {
89                let message = boxed_conversion.err().unwrap();
90                return Err(message);
91            }
92            let converted_second_byte = boxed_conversion.unwrap();
93
94
95            let shifted_converted_second_byte = converted_second_byte >> 4;
96            let _shifted_second_byte_as_string = format!("{shifted_converted_second_byte:b}");
97
98
99            let resulted_byte = shifted_converted_first_byte | shifted_converted_second_byte;
100            return Ok(vec![resulted_byte]);
101
102        }
103
104        if number_of_equal_signs == 1 {
105            let boxed_first_byte = text.chars().nth(0);
106            if boxed_first_byte.is_none() {
107                return Err("unexpected error, unable to get char at position 0".to_string());
108            }
109            let first_byte = boxed_first_byte.unwrap() as u8;
110            let _first_byte_as_string = format!("{first_byte:b}");
111
112            let boxed_conversion = Base64::convert_base64_char_to_number(first_byte as char);
113            if boxed_conversion.is_err() {
114                let message = boxed_conversion.err().unwrap();
115                return Err(message);
116            }
117            let converted_first_byte = boxed_conversion.unwrap();
118            let shifted_converted_first_byte = converted_first_byte << 2;
119            let _shifted_converted_first_byte_as_string = format!("{converted_first_byte:b}");
120
121
122
123            let boxed_second_byte = text.chars().nth(1);
124            if boxed_second_byte.is_none() {
125                return Err("unexpected error, unable to get char at position 1".to_string());
126            }
127            let second_byte = boxed_second_byte.unwrap() as u8;
128            let _second_byte_as_string = format!("{second_byte:b}");
129
130
131            let boxed_conversion = Base64::convert_base64_char_to_number(second_byte as char);
132            if boxed_conversion.is_err() {
133                let message = boxed_conversion.err().unwrap();
134                return Err(message);
135            }
136            let converted_second_byte = boxed_conversion.unwrap();
137
138
139            let shifted_converted_second_byte = converted_second_byte >> 4;
140            let _shifted_second_byte_as_string = format!("{shifted_converted_second_byte:b}");
141
142
143            let first_char_as_byte = shifted_converted_first_byte | shifted_converted_second_byte;
144
145
146
147
148            // second char
149            let second_char_part_one = (converted_second_byte & 0b00001111) << 4;
150            let boxed_third_byte = text.chars().nth(2);
151            if boxed_third_byte.is_none() {
152                return Err("unexpected error, unable to get char at position 2".to_string());
153            }
154            let third_byte = boxed_third_byte.unwrap() as u8;
155
156            let boxed_conversion = Base64::convert_base64_char_to_number(third_byte as char);
157            if boxed_conversion.is_err() {
158                let message = boxed_conversion.err().unwrap();
159                return Err(message);
160            }
161            let converted_third_byte = boxed_conversion.unwrap();
162            let shifted_third_byte = (0b00111100 & converted_third_byte)  >> 2;
163
164            let second_char_as_byte = shifted_third_byte | second_char_part_one;
165
166            return Ok(vec![first_char_as_byte, second_char_as_byte]);
167
168        }
169
170        if number_of_equal_signs == 0 {
171            let boxed_first_byte = text.chars().nth(0);
172            if boxed_first_byte.is_none() {
173                return Err("unexpected error, unable to get char at position 0".to_string());
174            }
175            let first_byte = boxed_first_byte.unwrap() as u8;
176            let _first_byte_as_string = format!("{first_byte:b}");
177
178            let boxed_conversion = Base64::convert_base64_char_to_number(first_byte as char);
179            if boxed_conversion.is_err() {
180                let message = boxed_conversion.err().unwrap();
181                return Err(message);
182            }
183            let converted_first_byte = boxed_conversion.unwrap();
184            let shifted_converted_first_byte = converted_first_byte << 2;
185            let _shifted_converted_first_byte_as_string = format!("{converted_first_byte:b}");
186
187
188
189            let boxed_second_byte = text.chars().nth(1);
190            if boxed_second_byte.is_none() {
191                return Err("unexpected error, unable to get char at position 1".to_string());
192            }
193            let second_byte = boxed_second_byte.unwrap() as u8;
194            let _second_byte_as_string = format!("{second_byte:b}");
195
196            let boxed_conversion = Base64::convert_base64_char_to_number(second_byte as char);
197            if boxed_conversion.is_err() {
198                let message = boxed_conversion.err().unwrap();
199                return Err(message);
200            }
201            let converted_second_byte = boxed_conversion.unwrap();
202
203
204            let shifted_converted_second_byte = converted_second_byte >> 4;
205            let _shifted_second_byte_as_string = format!("{shifted_converted_second_byte:b}");
206
207
208            let first_char_as_byte = shifted_converted_first_byte | shifted_converted_second_byte;
209
210
211
212
213            // second char
214            let second_char_part_one = (converted_second_byte & 0b00001111) << 4;
215            let boxed_third_byte = text.chars().nth(2);
216            if boxed_third_byte.is_none() {
217                return Err("unexpected error, unable to get char at position 2".to_string());
218            }
219            let third_byte = boxed_third_byte.unwrap() as u8;
220
221            let boxed_conversion = Base64::convert_base64_char_to_number(third_byte as char);
222            if boxed_conversion.is_err() {
223                let message = boxed_conversion.err().unwrap();
224                return Err(message);
225            }
226            let converted_third_byte = boxed_conversion.unwrap();
227            let shifted_third_byte = (0b00111100 & converted_third_byte)  >> 2;
228
229            let second_char_as_byte = shifted_third_byte | second_char_part_one;
230
231            let boxed_conversion = Base64::convert_base64_char_to_number(third_byte as char);
232            if boxed_conversion.is_err() {
233                let message = boxed_conversion.err().unwrap();
234                return Err(message);
235            }
236            let converted_third_byte = boxed_conversion.unwrap();
237            let masked_third_byte = converted_third_byte & 0b00000011;
238            let shifted_masked_third_byte = masked_third_byte << 6;
239
240
241            let boxed_fourth_byte = text.chars().nth(3);
242            if boxed_fourth_byte.is_none() {
243                return Err("unexpected error, unable to get char at position 3".to_string());
244            }
245            let fourth_byte = boxed_fourth_byte.unwrap() as u8;
246
247            let boxed_conversion = Base64::convert_base64_char_to_number(fourth_byte as char);
248            if boxed_conversion.is_err() {
249                let message = boxed_conversion.err().unwrap();
250                return Err(message);
251            }
252            let converted_fourth_byte = boxed_conversion.unwrap();
253            let masked_fourth_byte = converted_fourth_byte & 0b00111111;
254
255            let third_char_as_byte = shifted_masked_third_byte | masked_fourth_byte;
256
257
258            return Ok(vec![first_char_as_byte, second_char_as_byte, third_char_as_byte]);
259
260        }
261
262        Ok(result)
263    }
264
265    pub fn encode_sequence(bytes: &[u8]) -> Result<String, String> {
266        if bytes.len() > 3 {
267            return Err("sequence encodes at most 3 bytes at once".to_string());
268        }
269
270        if bytes.len() == 0 {
271            return Err("sequence encodes at least 1 byte".to_string());
272        }
273
274        if bytes.len() == 1 {
275            let boxed_byte = bytes.get(0);
276            if boxed_byte.is_none() {
277                return Err("byte at pos 1 is empty".to_string());
278            }
279
280            let byte = boxed_byte.unwrap();
281            let _byte_as_string = format!("{byte:b}");
282            let shifted_first_sextet = byte >> 2;
283            let _shifted_first_sextet_as_string = format!("{shifted_first_sextet:b}");
284
285            let shifted_second_sextet = (byte & 0b00000011) << 4;
286            let _shifted_first_sextet_as_string = format!("{shifted_second_sextet:b}");
287
288            let mut result_buffer: Vec<String> = vec![];
289
290            let boxed_encoded_char = Base64::convert_number_to_base64_char(shifted_first_sextet);
291            if boxed_encoded_char.is_err() {
292                return Err(boxed_encoded_char.err().unwrap());
293            }
294
295            result_buffer.push(boxed_encoded_char.unwrap().to_string());
296
297            let boxed_encoded_char = Base64::convert_number_to_base64_char(shifted_second_sextet);
298            if boxed_encoded_char.is_err() {
299                return Err(boxed_encoded_char.err().unwrap());
300            }
301
302            result_buffer.push(boxed_encoded_char.unwrap().to_string());
303
304            result_buffer.push(SYMBOL.equals.to_string());
305            result_buffer.push(SYMBOL.equals.to_string());
306
307            let result : String = result_buffer.join(SYMBOL.empty_string);
308            return Ok(result);
309        }
310
311        if bytes.len() == 2 {
312            let boxed_byte = bytes.get(0);
313            if boxed_byte.is_none() {
314                return Err("byte at pos 1 is empty".to_string());
315            }
316
317            let byte = boxed_byte.unwrap();
318            let _byte_as_string = format!("{byte:b}");
319            let shifted_first_sextet = byte >> 2;
320            let _shifted_first_sextet_as_string = format!("{shifted_first_sextet:b}");
321
322
323
324            let mut result_buffer: Vec<String> = vec![];
325
326            let boxed_encoded_char = Base64::convert_number_to_base64_char(shifted_first_sextet);
327            if boxed_encoded_char.is_err() {
328                return Err(boxed_encoded_char.err().unwrap());
329            }
330
331            let char : String =  boxed_encoded_char.unwrap().to_string();
332            result_buffer.push(char);
333
334
335            // base64 second sextet part 1 (from first u8)
336            let shifted_second_sextet_part_one = (byte & 0b00000011) << 4;
337            let _shifted_second_sextet_as_string = format!("{shifted_second_sextet_part_one:b}");
338
339
340            // base64 second sextet part 2 (from second u8)
341            let boxed_byte = bytes.get(1);
342            if boxed_byte.is_none() {
343                return Err("byte at pos 1 is empty".to_string());
344            }
345
346            let second_byte = boxed_byte.unwrap();
347            let shifted_second_byte_part_two = second_byte >> 4;
348
349
350            let second_sextet = shifted_second_sextet_part_one | shifted_second_byte_part_two;
351            let boxed_second_encoded_char = Base64::convert_number_to_base64_char(second_sextet);
352            if boxed_second_encoded_char.is_err() {
353                return Err(boxed_second_encoded_char.err().unwrap());
354            }
355
356            let char =  boxed_second_encoded_char.unwrap();
357            result_buffer.push(char.to_string());
358
359
360            // base64 third char
361            let base64_third_char = (second_byte & 0b00001111) << 2;
362            let boxed_third_encoded_char = Base64::convert_number_to_base64_char(base64_third_char);
363            if boxed_third_encoded_char.is_err() {
364                return Err(boxed_third_encoded_char.err().unwrap());
365            }
366            let char =  boxed_third_encoded_char.unwrap();
367            result_buffer.push(char.to_string());
368
369
370
371            result_buffer.push(SYMBOL.equals.to_string());
372
373            let result : String = result_buffer.join(SYMBOL.empty_string);
374            return Ok(result);
375        }
376
377        if bytes.len() == 3 {
378            let boxed_byte = bytes.get(0);
379            if boxed_byte.is_none() {
380                return Err("byte at pos 1 is empty".to_string());
381            }
382
383            let byte = boxed_byte.unwrap();
384            let _byte_as_string = format!("{byte:b}");
385            let shifted_first_sextet = byte >> 2;
386            let _shifted_first_sextet_as_string = format!("{shifted_first_sextet:b}");
387
388
389
390            let mut result_buffer: Vec<String> = vec![];
391
392            let boxed_encoded_char = Base64::convert_number_to_base64_char(shifted_first_sextet);
393            if boxed_encoded_char.is_err() {
394                return Err(boxed_encoded_char.err().unwrap());
395            }
396
397            let char : String =  boxed_encoded_char.unwrap().to_string();
398            result_buffer.push(char);
399
400
401            // base64 second sextet part 1 (from first u8)
402            let shifted_second_sextet_part_one = (byte & 0b00000011) << 4;
403            let _shifted_second_sextet_as_string = format!("{shifted_second_sextet_part_one:b}");
404
405
406            // base64 second sextet part 2 (from second u8)
407            let boxed_byte = bytes.get(1);
408            if boxed_byte.is_none() {
409                return Err("byte at pos 1 is empty".to_string());
410            }
411
412            let second_byte = boxed_byte.unwrap();
413            let shifted_second_byte_part_two = second_byte >> 4;
414
415
416            let second_sextet = shifted_second_sextet_part_one | shifted_second_byte_part_two;
417            let boxed_second_encoded_char = Base64::convert_number_to_base64_char(second_sextet);
418            if boxed_second_encoded_char.is_err() {
419                return Err(boxed_second_encoded_char.err().unwrap());
420            }
421
422            let char =  boxed_second_encoded_char.unwrap();
423            result_buffer.push(char.to_string());
424
425
426            // base64 third char
427            let base64_third_char = (second_byte & 0b00001111) << 2;
428
429
430            let boxed_byte = bytes.get(2);
431            if boxed_byte.is_none() {
432                return Err("byte at pos 1 is empty".to_string());
433            }
434
435            let third_byte = boxed_byte.unwrap();
436            let third_encoded_char_part2 = (third_byte & 0b11000000) >> 6;
437
438            let third_encoded_char = base64_third_char | third_encoded_char_part2;
439
440            let boxed_third_encoded_char = Base64::convert_number_to_base64_char(third_encoded_char);
441            if boxed_third_encoded_char.is_err() {
442                return Err(boxed_third_encoded_char.err().unwrap());
443            }
444            let char =  boxed_third_encoded_char.unwrap();
445            result_buffer.push(char.to_string());
446
447            let fourth_encoded_char = third_byte & 0b00111111;
448            let boxed_fourth_encoded_char = Base64::convert_number_to_base64_char(fourth_encoded_char);
449            if boxed_fourth_encoded_char.is_err() {
450                return Err(boxed_fourth_encoded_char.err().unwrap());
451            }
452            let char =  boxed_fourth_encoded_char.unwrap();
453            result_buffer.push(char.to_string());
454
455            let result : String = result_buffer.join(SYMBOL.empty_string);
456            return Ok(result);
457        }
458
459        Ok("".to_string())
460    }
461
462    pub fn convert_base64_char_to_number(char: char) -> Result<u8, String> {
463        let base64_char_list : Vec<char> = Base64::get_base64_char_list();
464        let mut map : HashMap<char, u8> = HashMap::new();
465
466        for (index, char) in base64_char_list.iter().enumerate() {
467            map.insert(*char, index as u8);
468        }
469
470        let boxed_get = map.get(&char);
471        if boxed_get.is_none() {
472            let message = format!("unable to get char number: {}", char);
473            return Err(message);
474        }
475        let index : &u8 = map.get(&char).unwrap();
476
477        Ok(*index)
478    }
479
480    pub fn get_base64_char_list() -> Vec<char> {
481        let mut base64_table : Vec<char> = vec![];
482
483        let mut uppercase = ('A'..='Z').into_iter().collect::<Vec<char>>();
484        base64_table.append(&mut uppercase);
485
486        let mut lowercase = ('a'..='z').into_iter().collect::<Vec<char>>();
487        base64_table.append(&mut lowercase);
488
489        let mut numbers = ('0'..='9').into_iter().collect::<Vec<char>>();
490        base64_table.append(&mut numbers);
491
492        base64_table.push('+');
493        base64_table.push('/');
494
495        base64_table
496    }
497
498    pub fn convert_number_to_base64_char(number: u8) -> Result<char, String> {
499        if number > 63 {
500            return Err("number exceeds range 0 - 63".to_string());
501        }
502
503        let base64_table : Vec<char> = Base64::get_base64_char_list();
504
505        let boxed_get : Option<char> = base64_table.get(number as usize).copied();
506        if boxed_get.is_none() {
507            return Err(format!("unable to convert number to base64 char: {}", number).to_string())
508        }
509
510        let char: char = boxed_get.unwrap();
511        Ok(char)
512    }
513}