buup/transformers/
base64_decode.rs1use crate::{Transform, TransformError, TransformerCategory};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub struct Base64Decode;
6
7impl Transform for Base64Decode {
8 fn name(&self) -> &'static str {
9 "Base64 Decode"
10 }
11
12 fn id(&self) -> &'static str {
13 "base64decode"
14 }
15
16 fn description(&self) -> &'static str {
17 "Decode Base64 text to plain text"
18 }
19
20 fn category(&self) -> TransformerCategory {
21 TransformerCategory::Decoder
22 }
23
24 fn transform(&self, input: &str) -> Result<String, TransformError> {
25 let decoded = base64_decode(input).map_err(|_| TransformError::Base64DecodeError)?;
26 String::from_utf8(decoded).map_err(|_| TransformError::Utf8Error)
27 }
28
29 fn default_test_input(&self) -> &'static str {
30 "SGVsbG8sIFdvcmxkIQ=="
31 }
32}
33
34pub(crate) fn base64_decode(input: &str) -> Result<Vec<u8>, &'static str> {
36 fn create_lookup_table() -> [i8; 256] {
38 let mut table = [-1i8; 256];
39 b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
40 .iter()
41 .enumerate()
42 .for_each(|(i, &c)| table[c as usize] = i as i8);
43 table
44 }
45
46 let lookup = create_lookup_table();
47 let input = input.trim().as_bytes();
48
49 let padding = input.iter().rev().take_while(|&&c| c == b'=').count();
51 let output_len = input.len() * 3 / 4 - padding;
52
53 let mut output = vec![0u8; output_len];
54 let mut output_index = 0;
55
56 for chunk in input.chunks(4) {
58 if chunk.len() < 2 {
59 return Err("Invalid base64 length");
60 }
61
62 let b0 = lookup[chunk[0] as usize];
64 let b1 = lookup[chunk[1] as usize];
65
66 if b0 < 0 || b1 < 0 {
68 return Err("Invalid base64 character");
69 }
70
71 if output_index < output_len {
73 output[output_index] = ((b0 << 2) | (b1 >> 4)) as u8;
74 output_index += 1;
75 }
76
77 if chunk.len() > 2 {
78 if chunk[2] == b'=' {
80 if chunk.len() < 4 || chunk[3] != b'=' {
81 return Err("Invalid base64 padding");
82 }
83 continue; }
85
86 let b2 = lookup[chunk[2] as usize];
87 if b2 < 0 {
88 return Err("Invalid base64 character");
89 }
90
91 if output_index < output_len {
92 output[output_index] = (((b1 & 0xF) << 4) | (b2 >> 2)) as u8;
93 output_index += 1;
94 }
95
96 if chunk.len() > 3 {
97 if chunk[3] == b'=' {
98 continue; }
100
101 let b3 = lookup[chunk[3] as usize];
102 if b3 < 0 {
103 return Err("Invalid base64 character");
104 }
105
106 if output_index < output_len {
107 output[output_index] = (((b2 & 0x3) << 6) | b3) as u8;
108 output_index += 1;
109 }
110 }
111 }
112 }
113
114 Ok(output)
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn test_base64_decode() {
123 let transformer = Base64Decode;
124 assert_eq!(
125 transformer
126 .transform(transformer.default_test_input())
127 .unwrap(),
128 "Hello, World!"
129 );
130 assert_eq!(transformer.transform("").unwrap(), "");
131 assert_eq!(transformer.transform("YQ==").unwrap(), "a");
132 }
133}