Coverage Report

Created: 2026-09-14 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/curlx/base64.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
25
/* Base64 encoding/decoding */
26
27
#include "curl_setup.h"
28
29
#include "curlx/base64.h"
30
31
/* ---- Base64 Encoding/Decoding Table --- */
32
const char curlx_base64encdec[] =
33
  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
34
35
/* The Base 64 encoding with a URL and filename safe alphabet, RFC 4648
36
   section 5 */
37
static const char base64url[] =
38
  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
39
40
static const unsigned char decodetable[256] = {
41
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
42
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
43
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
44
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
45
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
46
  0xff, 0xff, 0xff, 62,   0xff, 0xff, 0xff, 63,
47
  52,   53,   54,   55,   56,   57,   58,   59,
48
  60,   61,   0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
49
  0xff, 0,    1,    2,    3,    4,    5,    6,
50
  7,    8,    9,    10,   11,   12,   13,   14,
51
  15,   16,   17,   18,   19,   20,   21,   22,
52
  23,   24,   25,   0xff, 0xff, 0xff, 0xff, 0xff,
53
  0xff, 26,   27,   28,   29,   30,   31,   32,
54
  33,   34,   35,   36,   37,   38,   39,   40,
55
  41,   42,   43,   44,   45,   46,   47,   48,
56
  49,   50,   51,   0xff, 0xff, 0xff, 0xff, 0xff,
57
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
58
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
59
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
60
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
61
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
62
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
63
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
64
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
65
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
66
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
67
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
68
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
69
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
70
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
71
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
72
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
73
};
74
/*
75
 * curlx_base64_decode()
76
 *
77
 * Given a base64 null-terminated string at src, decode it and return a
78
 * pointer in *outptr to a newly allocated memory area holding decoded data.
79
 * Size of decoded data is returned in variable pointed by outlen.
80
 *
81
 * Returns CURLE_OK on success, otherwise specific error code. Function
82
 * output shall not be considered valid unless CURLE_OK is returned.
83
 *
84
 * When decoded data length is 0, returns NULL in *outptr.
85
 *
86
 * @unittest: 1302
87
 */
88
CURLcode curlx_base64_decode(const char *src,
89
                             uint8_t **outptr, size_t *outlen)
90
0
{
91
0
  size_t srclen = 0;
92
0
  size_t padding = 0;
93
0
  size_t i;
94
0
  size_t numQuantums;
95
0
  size_t fullQuantums;
96
0
  size_t rawlen = 0;
97
0
  unsigned char *pos;
98
0
  unsigned char *newstr;
99
100
0
  *outptr = NULL;
101
0
  *outlen = 0;
102
0
  srclen = strlen(src);
103
104
  /* Check the length of the input string is valid */
105
0
  if(!srclen || srclen % 4)
106
0
    return CURLE_BAD_CONTENT_ENCODING;
107
108
  /* srclen is at least 4 here */
109
0
  while(src[srclen - 1 - padding] == '=') {
110
    /* count padding characters */
111
0
    padding++;
112
    /* A maximum of two = padding characters is allowed */
113
0
    if(padding > 2)
114
0
      return CURLE_BAD_CONTENT_ENCODING;
115
0
  }
116
117
  /* Calculate the number of quantums */
118
0
  numQuantums = srclen / 4;
119
0
  fullQuantums = numQuantums - (padding ? 1 : 0);
120
121
  /* Calculate the size of the decoded string */
122
0
  rawlen = (numQuantums * 3) - padding;
123
124
  /* Allocate our buffer including room for a null-terminator */
125
0
  newstr = curlx_malloc(rawlen + 1);
126
0
  if(!newstr)
127
0
    return CURLE_OUT_OF_MEMORY;
128
129
0
  pos = newstr;
130
131
  /* Decode the complete quantums first */
132
0
  for(i = 0; i < fullQuantums; i++) {
133
0
    unsigned char v0 = decodetable[(unsigned char)src[0]];
134
0
    unsigned char v1 = decodetable[(unsigned char)src[1]];
135
0
    unsigned char v2 = decodetable[(unsigned char)src[2]];
136
0
    unsigned char v3 = decodetable[(unsigned char)src[3]];
137
0
    if((v0 | v1 | v2 | v3) & 0x80)
138
0
      goto bad;
139
0
    pos[0] = (unsigned char)((v0 << 2) | (v1 >> 4));
140
0
    pos[1] = (unsigned char)((v1 << 4) | (v2 >> 2));
141
0
    pos[2] = (unsigned char)((v2 << 6) | v3);
142
0
    pos += 3;
143
0
    src += 4;
144
0
  }
145
0
  if(padding) {
146
    /* this means either 8 or 16 bits output */
147
0
    unsigned char val;
148
0
    unsigned int x = 0;
149
0
    int j;
150
0
    size_t padc = 0;
151
0
    for(j = 0; j < 4; j++) {
152
0
      if(*src == '=') {
153
0
        x <<= 6;
154
0
        src++;
155
0
        if(++padc > padding)
156
          /* this is a badly placed '=' symbol! */
157
0
          goto bad;
158
0
      }
159
0
      else {
160
0
        val = decodetable[(unsigned char)*src++];
161
0
        if(val == 0xff) /* bad symbol */
162
0
          goto bad;
163
0
        x = (x << 6) | val;
164
0
      }
165
0
    }
166
0
    if(padding == 1)
167
0
      pos[1] = (unsigned char)((x >> 8) & 0xff);
168
0
    pos[0] = (unsigned char)((x >> 16) & 0xff);
169
0
    pos += 3 - padding;
170
0
  }
171
172
  /* null-terminate */
173
0
  *pos = '\0';
174
175
  /* Return the decoded data */
176
0
  *outptr = newstr;
177
0
  *outlen = rawlen;
178
179
0
  return CURLE_OK;
180
0
bad:
181
0
  curlx_free(newstr);
182
0
  return CURLE_BAD_CONTENT_ENCODING;
183
0
}
184
185
static CURLcode base64_encode(const char *table64,
186
                              uint8_t padbyte,
187
                              const uint8_t *inputbuff, size_t insize,
188
                              char **outptr, size_t *outlen)
189
1.11k
{
190
1.11k
  char *output;
191
1.11k
  char *base64data;
192
1.11k
  const unsigned char *in = (const unsigned char *)inputbuff;
193
194
1.11k
  *outptr = NULL;
195
1.11k
  *outlen = 0;
196
197
1.11k
  if(!insize)
198
0
    return CURLE_OK;
199
200
  /* safety precaution */
201
1.11k
  DEBUGASSERT(insize <= CURL_MAX_BASE64_INPUT);
202
1.11k
  if(insize > CURL_MAX_BASE64_INPUT)
203
0
    return CURLE_TOO_LARGE;
204
205
1.11k
  base64data = output = curlx_malloc(((insize + 2) / 3 * 4) + 1);
206
1.11k
  if(!output)
207
0
    return CURLE_OUT_OF_MEMORY;
208
209
5.48M
  while(insize >= 3) {
210
5.48M
    *output++ = table64[in[0] >> 2];
211
5.48M
    *output++ = table64[((in[0] & 0x03) << 4) | (in[1] >> 4)];
212
5.48M
    *output++ = table64[((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6)];
213
5.48M
    *output++ = table64[in[2] & 0x3F];
214
5.48M
    insize -= 3;
215
5.48M
    in += 3;
216
5.48M
  }
217
1.11k
  if(insize) {
218
    /* this is only one or two bytes now */
219
772
    *output++ = table64[in[0] >> 2];
220
772
    if(insize == 1) {
221
327
      *output++ = table64[((in[0] & 0x03) << 4)];
222
327
      if(padbyte) {
223
327
        *output++ = padbyte;
224
327
        *output++ = padbyte;
225
327
      }
226
327
    }
227
445
    else {
228
      /* insize == 2 */
229
445
      *output++ = table64[((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4)];
230
445
      *output++ = table64[((in[1] & 0x0F) << 2)];
231
445
      if(padbyte)
232
445
        *output++ = padbyte;
233
445
    }
234
772
  }
235
236
  /* null-terminate */
237
1.11k
  *output = '\0';
238
239
  /* Return the pointer to the new data (allocated memory) */
240
1.11k
  *outptr = base64data;
241
242
  /* Return the length of the new data */
243
1.11k
  *outlen = (size_t)(output - base64data);
244
245
1.11k
  return CURLE_OK;
246
1.11k
}
247
248
/*
249
 * curlx_base64_encode()
250
 *
251
 * Given a pointer to an input buffer and an input size, encode it and
252
 * return a pointer in *outptr to a newly allocated memory area holding
253
 * encoded data. Size of encoded data is returned in variable pointed by
254
 * outlen.
255
 *
256
 * Returns CURLE_OK on success, otherwise specific error code. Function
257
 * output shall not be considered valid unless CURLE_OK is returned.
258
 *
259
 * @unittest: 1302
260
 */
261
CURLcode curlx_base64_encode(const uint8_t *inputbuff, size_t insize,
262
                             char **outptr, size_t *outlen)
263
993
{
264
993
  return base64_encode(curlx_base64encdec, '=',
265
993
                       inputbuff, insize, outptr, outlen);
266
993
}
267
268
/*
269
 * curlx_base64url_encode()
270
 *
271
 * Given a pointer to an input buffer and an input size, encode it and
272
 * return a pointer in *outptr to a newly allocated memory area holding
273
 * encoded data. Size of encoded data is returned in variable pointed by
274
 * outlen.
275
 *
276
 * Input length of 0 indicates input buffer holds a null-terminated string.
277
 *
278
 * Returns CURLE_OK on success, otherwise specific error code. Function
279
 * output shall not be considered valid unless CURLE_OK is returned.
280
 *
281
 * @unittest: 1302
282
 */
283
CURLcode curlx_base64url_encode(const uint8_t *inputbuff, size_t insize,
284
                                char **outptr, size_t *outlen)
285
124
{
286
124
  return base64_encode(base64url, 0, inputbuff, insize, outptr, outlen);
287
124
}