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