/ src / util / encoding / base64.rs
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  }