base64.rs
1 /* This file is part of DarkFi (https://dark.fi) 2 * 3 * Copyright (C) 2020-2025 Dyne.org foundation 4 * Copyright (C) 2021-2023 Kavan Mevada (MIT) (https://github.com/kavanmevada/base64cr) 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU Affero General Public License as 8 * published by the Free Software Foundation, either version 3 of the 9 * License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU Affero General Public License for more details. 15 * 16 * You should have received a copy of the GNU Affero General Public License 17 * along with this program. If not, see <https://www.gnu.org/licenses/>. 18 */ 19 20 #![forbid(unsafe_code)] 21 22 macro_rules! seq4 { 23 [$($e:expr),*] => { [$($e,$e,$e,$e,)*] } 24 } 25 26 macro_rules! rep4 { 27 [$($e:expr),*] => { [$($e,)*$($e,)*$($e,)*$($e,)*] } 28 } 29 30 static E0: [char; 256] = seq4![ 31 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 32 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 33 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', 34 '5', '6', '7', '8', '9', '+', '/' 35 ]; 36 37 static E1: [char; 256] = rep4![ 38 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 39 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 40 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', 41 '5', '6', '7', '8', '9', '+', '/' 42 ]; 43 44 static E2: [char; 256] = E1; 45 46 const FF: u32 = 33554431; 47 48 static D0: [u32; 256] = [ 49 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 50 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 248, FF, FF, FF, 51 252, 208, 212, 216, 220, 224, 228, 232, 236, 240, 244, FF, FF, FF, FF, FF, FF, FF, 0, 4, 8, 12, 52 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, FF, 53 FF, FF, FF, FF, FF, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 148, 152, 156, 160, 54 164, 168, 172, 176, 180, 184, 188, 192, 196, 200, 204, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 55 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 56 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 57 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 58 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 59 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 60 FF, FF, FF, 61 ]; 62 63 static D1: [u32; 256] = [ 64 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 65 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 57347, FF, FF, FF, 66 61443, 16387, 20483, 24579, 28675, 32771, 36867, 40963, 45059, 49155, 53251, FF, FF, FF, FF, 67 FF, FF, FF, 0, 4096, 8192, 12288, 16384, 20480, 24576, 28672, 32768, 36864, 40960, 45056, 68 49152, 53248, 57344, 61440, 1, 4097, 8193, 12289, 16385, 20481, 24577, 28673, 32769, 36865, FF, 69 FF, FF, FF, FF, FF, 40961, 45057, 49153, 53249, 57345, 61441, 2, 4098, 8194, 12290, 16386, 70 20482, 24578, 28674, 32770, 36866, 40962, 45058, 49154, 53250, 57346, 61442, 3, 4099, 8195, 71 12291, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 72 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 73 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 74 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 75 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 76 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 77 ]; 78 79 static D2: [u32; 256] = [ 80 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 81 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 8392448, FF, FF, 82 FF, 12586752, 3328, 4197632, 8391936, 12586240, 3584, 4197888, 8392192, 12586496, 3840, 83 4198144, FF, FF, FF, FF, FF, FF, FF, 0, 4194304, 8388608, 12582912, 256, 4194560, 8388864, 84 12583168, 512, 4194816, 8389120, 12583424, 768, 4195072, 8389376, 12583680, 1024, 4195328, 85 8389632, 12583936, 1280, 4195584, 8389888, 12584192, 1536, 4195840, FF, FF, FF, FF, FF, FF, 86 8390144, 12584448, 1792, 4196096, 8390400, 12584704, 2048, 4196352, 8390656, 12584960, 2304, 87 4196608, 8390912, 12585216, 2560, 4196864, 8391168, 12585472, 2816, 4197120, 8391424, 12585728, 88 3072, 4197376, 8391680, 12585984, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 89 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 90 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 91 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 92 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 93 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 94 ]; 95 96 static D3: [u32; 256] = [ 97 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 98 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 4063232, FF, FF, 99 FF, 4128768, 3407872, 3473408, 3538944, 3604480, 3670016, 3735552, 3801088, 3866624, 3932160, 100 3997696, FF, FF, FF, FF, FF, FF, FF, 0, 65536, 131072, 196608, 262144, 327680, 393216, 458752, 101 524288, 589824, 655360, 720896, 786432, 851968, 917504, 983040, 1048576, 1114112, 1179648, 102 1245184, 1310720, 1376256, 1441792, 1507328, 1572864, 1638400, FF, FF, FF, FF, FF, FF, 1703936, 103 1769472, 1835008, 1900544, 1966080, 2031616, 2097152, 2162688, 2228224, 2293760, 2359296, 104 2424832, 2490368, 2555904, 2621440, 2686976, 2752512, 2818048, 2883584, 2949120, 3014656, 105 3080192, 3145728, 3211264, 3276800, 3342336, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 106 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 107 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 108 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 109 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 110 FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 111 FF, 112 ]; 113 114 /// Encode a byte slice into a base64 string 115 pub fn encode(data: &[u8]) -> String { 116 let len = data.len(); 117 118 let mut dest = vec![0u8; ((4 * len / 3) + 3) & !3]; 119 120 let mut i = 0; 121 let mut j = 0; 122 123 if len > 2 { 124 while i < len - 2 { 125 let t1 = data[i]; 126 let t2 = data[i + 1]; 127 let t3 = data[i + 2]; 128 129 dest[j] = E0[t1 as usize] as u8; 130 dest[j + 1] = E1[(((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)) as usize] as u8; 131 dest[j + 2] = E1[(((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)) as usize] as u8; 132 dest[j + 3] = E2[t3 as usize] as u8; 133 134 i += 3; 135 j += 4; 136 } 137 } 138 match len - i { 139 0 => {} 140 1 => { 141 let t1 = data[i]; 142 143 dest[j] = E0[t1 as usize] as u8; 144 dest[j + 1] = E1[((t1 & 0x03) << 4) as usize] as u8; 145 dest[j + 2] = b'='; 146 dest[j + 3] = b'='; 147 } 148 _ => { 149 /* case 2 */ 150 let t1 = data[i] as usize; 151 let t2 = data[i + 1] as usize; 152 153 dest[j] = E0[t1] as u8; 154 dest[j + 1] = E1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)] as u8; 155 dest[j + 2] = E2[(t2 & 0x0F) << 2] as u8; 156 dest[j + 3] = b'='; 157 } 158 } 159 160 String::from_utf8(dest).unwrap() 161 } 162 163 /// Tries to decode a base64 string into a byte vector. 164 /// Returns `None` if something fails. 165 pub fn decode(data: &str) -> Option<Vec<u8>> { 166 if !data.is_ascii() || data.is_empty() { 167 return None 168 } 169 170 let data = match data.len() % 4 { 171 1 => return None, // Invalid input 172 2 => format!("{data}=="), 173 3 => format!("{data}="), 174 _ => data.to_string(), // Full or already padded 175 }; 176 177 let data = data.as_bytes(); 178 179 let mut padding_count = 0; 180 for (i, &byte) in data.iter().enumerate() { 181 let valid = byte.is_ascii_uppercase() || 182 byte.is_ascii_lowercase() || 183 byte.is_ascii_digit() || 184 byte == b'+' || 185 byte == b'/' || 186 byte == b'='; 187 188 if !valid { 189 return None 190 } 191 192 if byte == b'=' { 193 padding_count += 1; 194 if i < data.len() - 2 { 195 return None 196 } 197 } else if padding_count > 0 { 198 return None 199 } 200 } 201 202 if padding_count > 2 { 203 return None 204 } 205 206 let mut len = data.len(); 207 208 if data[len - 1] == b'=' { 209 len -= 1; 210 if data[len - 1] == b'=' { 211 len -= 1; 212 } 213 } 214 215 let mut dest = vec![0u8; (3 * (data.len() / 4)) - (data.len() - len)]; 216 217 let leftover = len % 4; 218 let chunks = if leftover == 0 { len / 4 - 1 } else { len / 4 }; 219 220 let mut j = 0; 221 let mut k = 0; 222 for _ in 0..chunks { 223 let x: u32 = D0[data[k] as usize] | 224 D1[data[1 + k] as usize] | 225 D2[data[2 + k] as usize] | 226 D3[data[3 + k] as usize]; 227 228 dest[j] = x as u8; 229 dest[j + 1] = (x >> 8) as u8; 230 dest[j + 2] = (x >> 16) as u8; 231 232 j += 3; 233 k += 4; 234 } 235 236 match leftover { 237 0 => { 238 let x: u32 = D0[data[k] as usize] | 239 D1[data[1 + k] as usize] | 240 D2[data[2 + k] as usize] | 241 D3[data[3 + k] as usize]; 242 243 dest[j] = x as u8; 244 dest[j + 1] = (x >> 8) as u8; 245 dest[j + 2] = (x >> 16) as u8; 246 247 // (chunks + 1) * 3) 248 return Some(dest) 249 } 250 1 => { 251 /* with padding this is an impossible case */ 252 let x: u32 = D0[data[k] as usize]; // i.e. first char/byte in int 253 dest[j] = x as u8; 254 } 255 2 => { 256 // * case 2, 1 output byte */ 257 let x: u32 = D0[data[k] as usize] | D1[data[1 + k] as usize]; // i.e. first char 258 dest[j] = x as u8; 259 } 260 _ => { 261 let x: u32 = D0[data[k] as usize] | D1[data[1 + k] as usize] | D2[data[2 + k] as usize]; /* 0x3c */ 262 dest[j] = x as u8; 263 dest[j + 1] = (x >> 8) as u8; 264 } 265 } 266 267 // 3 * chunks + (6 * leftover) / 8 268 Some(dest) 269 } 270 271 #[cfg(test)] 272 mod tests { 273 use super::*; 274 275 #[test] 276 pub fn b64_encdec() { 277 const EXAMPLES: [(&[u8], &str); 3] = [ 278 (b"abc123!?$*&()'-=@~", "YWJjMTIzIT8kKiYoKSctPUB+"), 279 (b"gm world", "Z20gd29ybGQ="), 280 ( 281 b"Man is distinguished, not only by his reason, but by this singular passion from \ 282 other animals, which is a lust of the mind, that by a perseverance of delight \ 283 in the continued and indefatigable generation of knowledge, exceeds the short \ 284 vehemence of any carnal pleasure.", 285 "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz\ 286 IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg\ 287 dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu\ 288 dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo\ 289 ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=", 290 ), 291 ]; 292 293 for &(input, answer) in EXAMPLES.iter() { 294 let res = encode(input); 295 assert_eq!(answer, res); 296 297 let res = decode(answer).unwrap(); 298 assert_eq!(input, res); 299 } 300 301 // Unpadded input checks 302 assert_eq!(b"a".to_vec(), decode("YQ").unwrap()); 303 assert_eq!(b"a".to_vec(), decode("YQ=").unwrap()); 304 assert_eq!(b"a0".to_vec(), decode("YTA").unwrap()); 305 assert_eq!(b"gm world".to_vec(), decode("Z20gd29ybGQ").unwrap()); 306 307 // Malformed decode input checks 308 assert!(decode("").is_none()); 309 assert!(decode("a").is_none()); 310 assert!(decode("a=").is_none()); 311 assert!(decode("a==").is_none()); 312 assert!(decode("a===").is_none()); 313 assert!(decode("=").is_none()); 314 assert!(decode("==").is_none()); 315 assert!(decode("===").is_none()); 316 assert!(decode("====").is_none()); 317 assert!(decode("This is a random string.").is_none()); 318 } 319 }