1 /*
2 * Copyright (c) 2012 Tim Ruehsen
3 * Copyright (c) 2015-2021 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
isbase64(char c)64 static bool WGET_GCC_CONST isbase64(char c)
65 {
66 // isalnum(c) does not work for all locales
67 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 }
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 */
wget_base64_is_string(const char * src)78 bool wget_base64_is_string(const char *src)
79 {
80 if (src) {
81 while (isbase64(*src)) src++;
82
83 if (!*src || (*src == '=' && src[1]) || (*src == '=' && src[1] == '=' && src[2]))
84 return 1;
85 }
86
87 return 0;
88 }
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 */
wget_base64_decode(char * dst,const char * src,size_t n)101 size_t wget_base64_decode(char *dst, const char *src, size_t n)
102 {
103 const unsigned char *usrc = (const unsigned char *)src;
104 char *old = dst;
105 int extra;
106
107 // trim '=' at the end
108 while (n > 0 && !isbase64(src[n - 1]))
109 n--;
110
111 extra = n & 3;
112
113 for (n /= 4; n > 0; n--, usrc += 4) {
114 *dst++ = (char)(base64_2_bin[usrc[0]] << 2 | base64_2_bin[usrc[1]] >> 4);
115 *dst++ = (char)((base64_2_bin[usrc[1]]&0x0F) << 4 | base64_2_bin[usrc[2]] >> 2);
116 *dst++ = (char)((base64_2_bin[usrc[2]]&0x03) << 6 | base64_2_bin[usrc[3]]);
117 }
118
119 switch (extra) {
120 case 1:
121 // this should not happen
122 *dst++ = (char) (base64_2_bin[usrc[0]] << 2);
123 break;
124 case 2:
125 *dst++ = (char)(base64_2_bin[usrc[0]] << 2 | base64_2_bin[usrc[1]] >> 4);
126 *dst = (char)((base64_2_bin[usrc[1]]&0x0F) << 4);
127 if (*dst) dst++;
128 break;
129 case 3:
130 *dst++ = (char)(base64_2_bin[usrc[0]] << 2 | base64_2_bin[usrc[1]] >> 4);
131 *dst++ = (char)((base64_2_bin[usrc[1]]&0x0F) << 4 | base64_2_bin[usrc[2]] >> 2);
132 *dst = (char)((base64_2_bin[usrc[2]]&0x03) << 6);
133 if (*dst) dst++;
134 break;
135 default: // 0: ignore
136 break;
137 }
138
139 *dst = 0;
140 return (size_t) (dst - old);
141 }
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 */
wget_base64_decode_alloc(const char * src,size_t n,size_t * outlen)154 char *wget_base64_decode_alloc(const char *src, size_t n, size_t *outlen)
155 {
156 char *dst = wget_malloc(wget_base64_get_decoded_length(n));
157
158 if (!dst)
159 return NULL;
160
161 size_t _outlen = wget_base64_decode(dst, src, n);
162
163 if (outlen)
164 *outlen = _outlen;
165
166 return dst;
167 }
168
169 #define WGET_BASE64_URLENCODE 1
170
base64_encode(char * dst,const char * src,size_t n,int flags)171 static size_t base64_encode(char *dst, const char *src, size_t n, int flags)
172 {
173 static const char base64unsafe[64] =
174 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
175 static const char base64urlsafe[64] =
176 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
177
178 const char *base64 = (flags & WGET_BASE64_URLENCODE) ? base64urlsafe : base64unsafe;
179 const unsigned char *usrc = (const unsigned char *)src;
180 char *start = dst;
181 int extra = n % 3;
182
183 // convert line by line
184 for (n /= 3; n > 0; n--, usrc += 3) {
185 *dst++ = base64[usrc[0] >> 2];
186 *dst++ = base64[((usrc[0]&3) << 4) | (usrc[1] >> 4)];
187 *dst++ = base64[((usrc[1]&15) << 2) | (usrc[2] >> 6)];
188 *dst++ = base64[usrc[2]&0x3f];
189 }
190
191 // special case
192 if (extra == 1) {
193 *dst++ = base64[usrc[0] >> 2];
194 *dst++ = base64[(usrc[0]&3) << 4];
195 *dst++ = '=';
196 *dst++ = '=';
197 } else if (extra == 2) {
198 *dst++ = base64[usrc[0] >> 2];
199 *dst++ = base64[((usrc[0]&3) << 4) | (usrc[1] >> 4)];
200 *dst++ = base64[((usrc[1]&15) << 2)];
201 *dst++ = '=';
202 }
203
204 *dst = 0;
205
206 return (size_t) (dst - start);
207 }
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 */
wget_base64_encode(char * dst,const char * src,size_t n)220 size_t wget_base64_encode(char *dst, const char *src, size_t n)
221 {
222 return base64_encode(dst, src, n, 0);
223 }
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 */
wget_base64_urlencode(char * dst,const char * src,size_t n)236 size_t wget_base64_urlencode(char *dst, const char *src, size_t n)
237 {
238 return base64_encode(dst, src, n, WGET_BASE64_URLENCODE);
239 }
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 */
wget_base64_encode_alloc(const char * src,size_t n)251 char *wget_base64_encode_alloc(const char *src, size_t n)
252 {
253 char *dst = wget_malloc(wget_base64_get_encoded_length(n));
254
255 if (dst)
256 wget_base64_encode(dst, src, n);
257
258 return dst;
259 }
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 */
wget_base64_encode_vprintf_alloc(const char * fmt,va_list args)271 char *wget_base64_encode_vprintf_alloc(const char *fmt, va_list args)
272 {
273 char *data = NULL;
274 size_t n;
275
276 n = wget_vasprintf(&data, fmt, args);
277
278 if (data) {
279 char *dst = wget_base64_encode_alloc(data, n);
280 xfree(data);
281 return dst;
282 }
283
284 return NULL;
285 }
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 */
wget_base64_encode_printf_alloc(const char * fmt,...)297 char *wget_base64_encode_printf_alloc(const char *fmt, ...)
298 {
299 char *dst;
300 va_list args;
301
302 va_start(args, fmt);
303 dst = wget_base64_encode_vprintf_alloc(fmt, args);
304 va_end(args);
305
306 return dst;
307 }
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 */
wget_base64_get_decoded_length(size_t len)316 size_t wget_base64_get_decoded_length(size_t len)
317 {
318 return ((len + 3) / 4) * 3 + 1;
319 }
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 */
wget_base64_get_encoded_length(size_t len)329 size_t wget_base64_get_encoded_length(size_t len)
330 {
331 return ((len + 2) / 3) * 4 + 1;
332 }
333
334 /**@}*/
335