Coverage Report

Created: 2025-01-28 06:58

/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
/**@}*/