darkfi/util/encoding/
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
22macro_rules! seq4 {
23    [$($e:expr),*] => { [$($e,$e,$e,$e,)*] }
24}
25
26macro_rules! rep4 {
27    [$($e:expr),*] => { [$($e,)*$($e,)*$($e,)*$($e,)*] }
28}
29
30static 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
37static 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
44static E2: [char; 256] = E1;
45
46const FF: u32 = 33554431;
47
48static 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
63static 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
79static 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
96static 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
115pub 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.
165pub fn decode(data: &str) -> Option<Vec<u8>> {
166    if !data.is_ascii() {
167        return None
168    }
169
170    let data = data.as_bytes();
171
172    let mut len = data.len();
173
174    if data[len - 1] == b'=' {
175        len -= 1;
176        if data[len - 1] == b'=' {
177            len -= 1;
178        }
179    }
180
181    let mut dest = vec![0u8; (3 * (data.len() / 4)) - (data.len() - len)];
182
183    let leftover = len % 4;
184    let chunks = if leftover == 0 { len / 4 - 1 } else { len / 4 };
185
186    let mut j = 0;
187    let mut k = 0;
188    for _ in 0..chunks {
189        let x: u32 = D0[data[k] as usize] |
190            D1[data[1 + k] as usize] |
191            D2[data[2 + k] as usize] |
192            D3[data[3 + k] as usize];
193
194        dest[j] = x as u8;
195        dest[j + 1] = (x >> 8) as u8;
196        dest[j + 2] = (x >> 16) as u8;
197
198        j += 3;
199        k += 4;
200    }
201
202    match leftover {
203        0 => {
204            let x: u32 = D0[data[k] as usize] |
205                D1[data[1 + k] as usize] |
206                D2[data[2 + k] as usize] |
207                D3[data[3 + k] as usize];
208
209            dest[j] = x as u8;
210            dest[j + 1] = (x >> 8) as u8;
211            dest[j + 2] = (x >> 16) as u8;
212
213            // (chunks + 1) * 3)
214            return Some(dest)
215        }
216        1 => {
217            /* with padding this is an impossible case */
218            let x: u32 = D0[data[k] as usize]; // i.e. first char/byte in int
219            dest[j] = x as u8;
220        }
221        2 => {
222            // * case 2, 1  output byte */
223            let x: u32 = D0[data[k] as usize] | D1[data[1 + k] as usize]; // i.e. first char
224            dest[j] = x as u8;
225        }
226        _ => {
227            let x: u32 = D0[data[k] as usize] | D1[data[1 + k] as usize] | D2[data[2 + k] as usize]; /* 0x3c */
228            dest[j] = x as u8;
229            dest[j + 1] = (x >> 8) as u8;
230        }
231    }
232
233    // 3 * chunks + (6 * leftover) / 8
234    Some(dest)
235}
236
237#[cfg(test)]
238mod tests {
239    use super::*;
240
241    #[test]
242    pub fn b64_encdec() {
243        const EXAMPLES: [(&[u8], &str); 3] = [
244            (b"abc123!?$*&()'-=@~", "YWJjMTIzIT8kKiYoKSctPUB+"),
245            (b"gm world", "Z20gd29ybGQ="),
246            (
247                b"Man is distinguished, not only by his reason, but by this singular passion from \
248            other animals, which is a lust of the mind, that by a perseverance of delight \
249            in the continued and indefatigable generation of knowledge, exceeds the short \
250            vehemence of any carnal pleasure.",
251                "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz\
252            IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg\
253            dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu\
254            dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo\
255            ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=",
256            ),
257        ];
258
259        for &(input, answer) in EXAMPLES.iter() {
260            let res = encode(input);
261            assert_eq!(answer, res);
262
263            let res = decode(answer).unwrap();
264            assert_eq!(input, res);
265        }
266    }
267}