rust_auth_utils/
base64.rs1use std::collections::HashMap;
4
5fn get_alphabet(url_safe: bool) -> &'static str {
9 if url_safe {
10 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
11 } else {
12 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
13 }
14}
15
16fn create_decode_map(alphabet: &str) -> HashMap<char, u8> {
20 alphabet
21 .chars()
22 .enumerate()
23 .map(|(i, c)| (c, i as u8))
24 .collect()
25}
26
27fn base64_encode(data: &[u8], alphabet: &str, padding: bool) -> String {
31 let mut result = String::new();
32 let mut buffer = 0u32;
33 let mut shift = 0;
34
35 for &byte in data {
36 buffer = (buffer << 8) | (byte as u32);
37 shift += 8;
38 while shift >= 6 {
39 shift -= 6;
40 result.push(
41 alphabet
42 .chars()
43 .nth(((buffer >> shift) & 0x3f) as usize)
44 .unwrap(),
45 );
46 }
47 }
48
49 if shift > 0 {
50 result.push(
51 alphabet
52 .chars()
53 .nth(((buffer << (6 - shift)) & 0x3f) as usize)
54 .unwrap(),
55 );
56 }
57
58 if padding {
59 let pad_count = (4 - (result.len() % 4)) % 4;
60 result.extend(std::iter::repeat('=').take(pad_count));
61 }
62
63 result
64}
65
66fn base64_decode(data: &str, alphabet: &str) -> Result<Vec<u8>, String> {
70 let decode_map = create_decode_map(alphabet);
71 let mut result = Vec::new();
72 let mut buffer = 0u32;
73 let mut bits_collected = 0;
74
75 for c in data.chars().take_while(|&c| c != '=') {
76 let value = decode_map
77 .get(&c)
78 .ok_or_else(|| format!("Invalid Base64 character: {}", c))?;
79
80 buffer = (buffer << 6) | (*value as u32);
81 bits_collected += 6;
82
83 if bits_collected >= 8 {
84 bits_collected -= 8;
85 result.push(((buffer >> bits_collected) & 0xff) as u8);
86 }
87 }
88
89 Ok(result)
90}
91
92#[derive(Default)]
93pub struct Base64;
94
95impl Base64 {
96 pub fn encode(data: &[u8], padding: Option<bool>) -> String {
100 let alphabet = get_alphabet(false);
101 base64_encode(data, alphabet, padding.unwrap_or(true))
102 }
103
104 pub fn decode(data: &str) -> Result<Vec<u8>, String> {
108 let url_safe = data.contains('-') || data.contains('_');
109 let alphabet = get_alphabet(url_safe);
110 base64_decode(data, alphabet)
111 }
112
113 pub fn encode_string(data: &str, padding: Option<bool>) -> String {
117 Self::encode(data.as_bytes(), padding)
118 }
119
120 pub fn decode_string(data: &str) -> Result<String, String> {
124 let bytes = Self::decode(data)?;
125 String::from_utf8(bytes).map_err(|e| format!("Invalid UTF-8 sequence: {}", e))
126 }
127}
128
129#[derive(Default)]
130pub struct Base64Url;
131
132impl Base64Url {
133 pub fn encode(data: &[u8], padding: Option<bool>) -> String {
137 let alphabet = get_alphabet(true);
138 base64_encode(data, alphabet, padding.unwrap_or(true))
139 }
140
141 pub fn decode(data: &str) -> Result<Vec<u8>, String> {
145 let alphabet = get_alphabet(true);
146 base64_decode(data, alphabet)
147 }
148
149 pub fn encode_string(data: &str, padding: Option<bool>) -> String {
153 Self::encode(data.as_bytes(), padding)
154 }
155
156 pub fn decode_string(data: &str) -> Result<String, String> {
160 let bytes = Self::decode(data)?;
161 String::from_utf8(bytes).map_err(|e| format!("Invalid UTF-8 sequence: {}", e))
162 }
163}