/src/wget2/libwget/base64.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2012 Tim Ruehsen |
3 | | * Copyright (c) 2015-2024 Free Software Foundation, Inc. |
4 | | * |
5 | | * This file is part of libwget. |
6 | | * |
7 | | * Libwget is free software: you can redistribute it and/or modify |
8 | | * it under the terms of the GNU Lesser General Public License as published by |
9 | | * the Free Software Foundation, either version 3 of the License, or |
10 | | * (at your option) any later version. |
11 | | * |
12 | | * Libwget is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU Lesser General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU Lesser General Public License |
18 | | * along with libwget. If not, see <https://www.gnu.org/licenses/>. |
19 | | * |
20 | | * |
21 | | * base64 conversion routines |
22 | | * |
23 | | * Changelog |
24 | | * 21.12.2012 Tim Ruehsen created |
25 | | * |
26 | | */ |
27 | | |
28 | | #include <config.h> |
29 | | |
30 | | #include <stddef.h> |
31 | | #include <stdarg.h> |
32 | | |
33 | | #include <wget.h> |
34 | | #include "private.h" |
35 | | |
36 | | /** |
37 | | * \file |
38 | | * \brief Base64 functions |
39 | | * \defgroup libwget-base64 Base64 functions |
40 | | * @{ |
41 | | * |
42 | | * This is a collection base64 encoding/decoding functions used in Wget2. |
43 | | */ |
44 | | |
45 | | static const unsigned char base64_2_bin[256] = { |
46 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
47 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
48 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 62, 0, 63, |
49 | | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, |
50 | | 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
51 | | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 63, |
52 | | 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, |
53 | | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0, |
54 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
55 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
56 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
57 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
58 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
59 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
60 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
61 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |
62 | | }; |
63 | | |
64 | | static bool WGET_GCC_CONST isbase64(char c) |
65 | 1.24k | { |
66 | | // isalnum(c) does not work for all locales |
67 | 1.24k | return base64_2_bin[(unsigned char) c] != 0; |
68 | | // return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '+' || c == '/' || c == '-' || c == '_'; |
69 | 1.24k | } |
70 | | |
71 | | /** |
72 | | * \param[in] src String to be checked |
73 | | * \return 1 if \p src is a base64 string, 0 otherwise |
74 | | * |
75 | | * Checks whether \p src is a base64 string. |
76 | | * Returns 0 if \p src is NULL. |
77 | | */ |
78 | | bool wget_base64_is_string(const char *src) |
79 | 0 | { |
80 | 0 | if (src) { |
81 | 0 | while (isbase64(*src)) src++; |
82 | |
|
83 | 0 | if (!*src || (*src == '=' && src[1]) || (*src == '=' && src[1] == '=' && src[2])) |
84 | 0 | return 1; |
85 | 0 | } |
86 | | |
87 | 0 | return 0; |
88 | 0 | } |
89 | | |
90 | | /** |
91 | | * \param[out] dst Output buffer |
92 | | * \param[in] src Base64 string to be decoded |
93 | | * \param[in] n Length of \p src |
94 | | * \return Number of bytes written into \p dst |
95 | | * |
96 | | * Decodes \p n bytes of the base64 string \p src. |
97 | | * The decoded bytes are written into \p dst and are 0-terminated. |
98 | | * |
99 | | * The size of \p dst has to be at minimum ((\p n + 3) / 4) * 3 + 1 bytes. |
100 | | */ |
101 | | size_t wget_base64_decode(char *dst, const char *src, size_t n) |
102 | 1.71k | { |
103 | 1.71k | const unsigned char *usrc = (const unsigned char *)src; |
104 | 1.71k | char *old = dst; |
105 | 1.71k | int extra; |
106 | | |
107 | | // trim '=' at the end |
108 | 2.30k | while (n > 0 && !isbase64(src[n - 1])) |
109 | 593 | n--; |
110 | | |
111 | 1.71k | extra = n & 3; |
112 | | |
113 | 3.29k | for (n /= 4; n > 0; n--, usrc += 4) { |
114 | 1.58k | *dst++ = (char)(base64_2_bin[usrc[0]] << 2 | base64_2_bin[usrc[1]] >> 4); |
115 | 1.58k | *dst++ = (char)((base64_2_bin[usrc[1]]&0x0F) << 4 | base64_2_bin[usrc[2]] >> 2); |
116 | 1.58k | *dst++ = (char)((base64_2_bin[usrc[2]]&0x03) << 6 | base64_2_bin[usrc[3]]); |
117 | 1.58k | } |
118 | | |
119 | 1.71k | switch (extra) { |
120 | 444 | case 1: |
121 | | // this should not happen |
122 | 444 | *dst++ = (char) (base64_2_bin[usrc[0]] << 2); |
123 | 444 | break; |
124 | 94 | case 2: |
125 | 94 | *dst++ = (char)(base64_2_bin[usrc[0]] << 2 | base64_2_bin[usrc[1]] >> 4); |
126 | 94 | *dst = (char)((base64_2_bin[usrc[1]]&0x0F) << 4); |
127 | 94 | if (*dst) dst++; |
128 | 94 | break; |
129 | 103 | case 3: |
130 | 103 | *dst++ = (char)(base64_2_bin[usrc[0]] << 2 | base64_2_bin[usrc[1]] >> 4); |
131 | 103 | *dst++ = (char)((base64_2_bin[usrc[1]]&0x0F) << 4 | base64_2_bin[usrc[2]] >> 2); |
132 | 103 | *dst = (char)((base64_2_bin[usrc[2]]&0x03) << 6); |
133 | 103 | if (*dst) dst++; |
134 | 103 | break; |
135 | 1.07k | default: // 0: ignore |
136 | 1.07k | break; |
137 | 1.71k | } |
138 | | |
139 | 1.71k | *dst = 0; |
140 | 1.71k | return (size_t) (dst - old); |
141 | 1.71k | } |
142 | | |
143 | | /** |
144 | | * \param[in] src Base64 string to be decoded |
145 | | * \param[in] n Length of \p src |
146 | | * \param[out] outlen Length of returned string, may be NULL. |
147 | | * \return Decoded bytes, zero terminated. Returns NULL if memory allocation failed. |
148 | | * |
149 | | * Decodes \p n bytes of the base64 string \p src. |
150 | | * The decoded bytes are returned in an allocated buffer. |
151 | | * |
152 | | * You should free() the returned string when not needed any more. |
153 | | */ |
154 | | char *wget_base64_decode_alloc(const char *src, size_t n, size_t *outlen) |
155 | 0 | { |
156 | 0 | char *dst = wget_malloc(wget_base64_get_decoded_length(n)); |
157 | |
|
158 | 0 | if (!dst) |
159 | 0 | return NULL; |
160 | | |
161 | 0 | size_t _outlen = wget_base64_decode(dst, src, n); |
162 | |
|
163 | 0 | if (outlen) |
164 | 0 | *outlen = _outlen; |
165 | |
|
166 | 0 | return dst; |
167 | 0 | } |
168 | | |
169 | 0 | #define WGET_BASE64_URLENCODE 1 |
170 | | |
171 | | static size_t base64_encode(char *dst, const char *src, size_t n, int flags) |
172 | 0 | { |
173 | 0 | static const char base64unsafe[64] = |
174 | 0 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
175 | 0 | static const char base64urlsafe[64] = |
176 | 0 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; |
177 | |
|
178 | 0 | const char *base64 = (flags & WGET_BASE64_URLENCODE) ? base64urlsafe : base64unsafe; |
179 | 0 | const unsigned char *usrc = (const unsigned char *)src; |
180 | 0 | char *start = dst; |
181 | 0 | int extra = n % 3; |
182 | | |
183 | | // convert line by line |
184 | 0 | for (n /= 3; n > 0; n--, usrc += 3) { |
185 | 0 | *dst++ = base64[usrc[0] >> 2]; |
186 | 0 | *dst++ = base64[((usrc[0]&3) << 4) | (usrc[1] >> 4)]; |
187 | 0 | *dst++ = base64[((usrc[1]&15) << 2) | (usrc[2] >> 6)]; |
188 | 0 | *dst++ = base64[usrc[2]&0x3f]; |
189 | 0 | } |
190 | | |
191 | | // special case |
192 | 0 | if (extra == 1) { |
193 | 0 | *dst++ = base64[usrc[0] >> 2]; |
194 | 0 | *dst++ = base64[(usrc[0]&3) << 4]; |
195 | 0 | *dst++ = '='; |
196 | 0 | *dst++ = '='; |
197 | 0 | } else if (extra == 2) { |
198 | 0 | *dst++ = base64[usrc[0] >> 2]; |
199 | 0 | *dst++ = base64[((usrc[0]&3) << 4) | (usrc[1] >> 4)]; |
200 | 0 | *dst++ = base64[((usrc[1]&15) << 2)]; |
201 | 0 | *dst++ = '='; |
202 | 0 | } |
203 | |
|
204 | 0 | *dst = 0; |
205 | |
|
206 | 0 | return (size_t) (dst - start); |
207 | 0 | } |
208 | | |
209 | | /** |
210 | | * \param[out] dst Base64 output string |
211 | | * \param[in] src Input buffer |
212 | | * \param[in] n Number of bytes to be encoded |
213 | | * \return Length of output string \p dst |
214 | | * |
215 | | * Encodes \p n bytes from \p src into a base64 string. |
216 | | * The encoded string is written into \p dst (0-terminated). |
217 | | * |
218 | | * The length of \p dst has to be at minimum ((\p n + 2) / 3) * 4 + 1 bytes. |
219 | | */ |
220 | | size_t wget_base64_encode(char *dst, const char *src, size_t n) |
221 | 0 | { |
222 | 0 | return base64_encode(dst, src, n, 0); |
223 | 0 | } |
224 | | |
225 | | /** |
226 | | * \param[out] dst Base64 output string (URL and filename safe) |
227 | | * \param[in] src Input buffer |
228 | | * \param[in] n Number of bytes to be encoded |
229 | | * \return Length of output string \p dst |
230 | | * |
231 | | * Encodes \p n bytes from \p src into a base64 URL and filename safe string (see RFC 4648, 5.). |
232 | | * The encoded string is written into \p dst (0-terminated). |
233 | | * |
234 | | * The length of \p dst has to be at minimum ((\p n + 2) / 3) * 4 + 1 bytes. |
235 | | */ |
236 | | size_t wget_base64_urlencode(char *dst, const char *src, size_t n) |
237 | 0 | { |
238 | 0 | return base64_encode(dst, src, n, WGET_BASE64_URLENCODE); |
239 | 0 | } |
240 | | |
241 | | /** |
242 | | * \param[in] src Input buffer |
243 | | * \param[in] n Number of bytes to be encoded |
244 | | * \return Base64 encoded string or NULL if memory allocation failed |
245 | | * |
246 | | * Encodes \p n bytes from input buffer \p src. |
247 | | * The encoded string is returned in an allocated buffer. |
248 | | * |
249 | | * You should free() the returned string when not needed any more. |
250 | | */ |
251 | | char *wget_base64_encode_alloc(const char *src, size_t n) |
252 | 0 | { |
253 | 0 | char *dst = wget_malloc(wget_base64_get_encoded_length(n)); |
254 | |
|
255 | 0 | if (dst) |
256 | 0 | wget_base64_encode(dst, src, n); |
257 | |
|
258 | 0 | return dst; |
259 | 0 | } |
260 | | |
261 | | /** |
262 | | * \param[in] fmt Printf-like format string |
263 | | * \param[in] args Argument list |
264 | | * \return Base64 encoded string |
265 | | * |
266 | | * Encodes the string constructed by \p fmt and \p args. |
267 | | * The encoded string is returned in an allocated buffer. |
268 | | * |
269 | | * You should free() the returned string when not needed any more. |
270 | | */ |
271 | | char *wget_base64_encode_vprintf_alloc(const char *fmt, va_list args) |
272 | 0 | { |
273 | 0 | char *data = NULL; |
274 | 0 | size_t n; |
275 | |
|
276 | 0 | n = wget_vasprintf(&data, fmt, args); |
277 | |
|
278 | 0 | if (data) { |
279 | 0 | char *dst = wget_base64_encode_alloc(data, n); |
280 | 0 | xfree(data); |
281 | 0 | return dst; |
282 | 0 | } |
283 | | |
284 | 0 | return NULL; |
285 | 0 | } |
286 | | |
287 | | /** |
288 | | * \param[in] fmt Printf-like format string |
289 | | * \param[in] ... Argument list |
290 | | * \return Base64 encoded string |
291 | | * |
292 | | * Encodes the string constructed by \p fmt and the arguments. |
293 | | * The encoded string is returned in an allocated buffer. |
294 | | * |
295 | | * You should free() the returned string when not needed any more. |
296 | | */ |
297 | | char *wget_base64_encode_printf_alloc(const char *fmt, ...) |
298 | 0 | { |
299 | 0 | char *dst; |
300 | 0 | va_list args; |
301 | |
|
302 | 0 | va_start(args, fmt); |
303 | 0 | dst = wget_base64_encode_vprintf_alloc(fmt, args); |
304 | 0 | va_end(args); |
305 | |
|
306 | 0 | return dst; |
307 | 0 | } |
308 | | |
309 | | /** |
310 | | * \fn static inline size_t wget_base64_get_decoded_length(size_t len) |
311 | | * \param[in] len Length of base64 sequence |
312 | | * \return Number of decoded bytes plus one (for 0-byte termination) |
313 | | * |
314 | | * Calculate the number of bytes needed for decoding a base64 sequence with length \p len. |
315 | | */ |
316 | | size_t wget_base64_get_decoded_length(size_t len) |
317 | 1.71k | { |
318 | 1.71k | return ((len + 3) / 4) * 3 + 1; |
319 | 1.71k | } |
320 | | |
321 | | /** |
322 | | * \fn static inline size_t wget_base64_get_encoded_length(size_t len) |
323 | | * \param[in] len Number of (un-encoded) bytes |
324 | | * \return Length of base64 encoding plus one (for 0-byte termination) |
325 | | * |
326 | | * Calculate the number of bytes needed for base64 encoding a byte sequence with length \p len, |
327 | | * including the padding and 0-termination bytes. |
328 | | */ |
329 | | size_t wget_base64_get_encoded_length(size_t len) |
330 | 0 | { |
331 | 0 | return ((len + 2) / 3) * 4 + 1; |
332 | 0 | } |
333 | | |
334 | | /**@}*/ |