Skip to main content

rust_web_server/core/base64/
mod.rs

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