xref: /curl/lib/base64.c (revision 03cf1c7b)
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 #if !defined(CURL_DISABLE_HTTP_AUTH) || defined(USE_SSH) || \
30   !defined(CURL_DISABLE_LDAP) || \
31   !defined(CURL_DISABLE_SMTP) || \
32   !defined(CURL_DISABLE_POP3) || \
33   !defined(CURL_DISABLE_IMAP) || \
34   !defined(CURL_DISABLE_DIGEST_AUTH) || \
35   !defined(CURL_DISABLE_DOH) || defined(USE_SSL) || defined(BUILDING_CURL)
36 #include "curl/curl.h"
37 #include "warnless.h"
38 #include "curl_base64.h"
39 
40 /* The last 2 #include files should be in this order */
41 #ifdef BUILDING_LIBCURL
42 #include "curl_memory.h"
43 #endif
44 #include "memdebug.h"
45 
46 /* ---- Base64 Encoding/Decoding Table --- */
47 /* Padding character string starts at offset 64. */
48 static const char base64encdec[]=
49   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
50 
51 /* The Base 64 encoding with a URL and filename safe alphabet, RFC 4648
52    section 5 */
53 static const char base64url[]=
54   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
55 
56 static const unsigned char decodetable[] =
57 { 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255,
58   255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
59   17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28,
60   29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
61   48, 49, 50, 51 };
62 /*
63  * Curl_base64_decode()
64  *
65  * Given a base64 NUL-terminated string at src, decode it and return a
66  * pointer in *outptr to a newly allocated memory area holding decoded
67  * data. Size of decoded data is returned in variable pointed by outlen.
68  *
69  * Returns CURLE_OK on success, otherwise specific error code. Function
70  * output shall not be considered valid unless CURLE_OK is returned.
71  *
72  * When decoded data length is 0, returns NULL in *outptr.
73  *
74  * @unittest: 1302
75  */
Curl_base64_decode(const char * src,unsigned char ** outptr,size_t * outlen)76 CURLcode Curl_base64_decode(const char *src,
77                             unsigned char **outptr, size_t *outlen)
78 {
79   size_t srclen = 0;
80   size_t padding = 0;
81   size_t i;
82   size_t numQuantums;
83   size_t fullQuantums;
84   size_t rawlen = 0;
85   unsigned char *pos;
86   unsigned char *newstr;
87   unsigned char lookup[256];
88 
89   *outptr = NULL;
90   *outlen = 0;
91   srclen = strlen(src);
92 
93   /* Check the length of the input string is valid */
94   if(!srclen || srclen % 4)
95     return CURLE_BAD_CONTENT_ENCODING;
96 
97   /* srclen is at least 4 here */
98   while(src[srclen - 1 - padding] == '=') {
99     /* count padding characters */
100     padding++;
101     /* A maximum of two = padding characters is allowed */
102     if(padding > 2)
103       return CURLE_BAD_CONTENT_ENCODING;
104   }
105 
106   /* Calculate the number of quantums */
107   numQuantums = srclen / 4;
108   fullQuantums = numQuantums - (padding ? 1 : 0);
109 
110   /* Calculate the size of the decoded string */
111   rawlen = (numQuantums * 3) - padding;
112 
113   /* Allocate our buffer including room for a null-terminator */
114   newstr = malloc(rawlen + 1);
115   if(!newstr)
116     return CURLE_OUT_OF_MEMORY;
117 
118   pos = newstr;
119 
120   memset(lookup, 0xff, sizeof(lookup));
121   memcpy(&lookup['+'], decodetable, sizeof(decodetable));
122   /* replaces
123   {
124     unsigned char c;
125     const unsigned char *p = (const unsigned char *)base64encdec;
126     for(c = 0; *p; c++, p++)
127       lookup[*p] = c;
128   }
129   */
130 
131   /* Decode the complete quantums first */
132   for(i = 0; i < fullQuantums; i++) {
133     unsigned char val;
134     unsigned int x = 0;
135     int j;
136 
137     for(j = 0; j < 4; j++) {
138       val = lookup[(unsigned char)*src++];
139       if(val == 0xff) /* bad symbol */
140         goto bad;
141       x = (x << 6) | val;
142     }
143     pos[2] = x & 0xff;
144     pos[1] = (x >> 8) & 0xff;
145     pos[0] = (x >> 16) & 0xff;
146     pos += 3;
147   }
148   if(padding) {
149     /* this means either 8 or 16 bits output */
150     unsigned char val;
151     unsigned int x = 0;
152     int j;
153     size_t padc = 0;
154     for(j = 0; j < 4; j++) {
155       if(*src == '=') {
156         x <<= 6;
157         src++;
158         if(++padc > padding)
159           /* this is a badly placed '=' symbol! */
160           goto bad;
161       }
162       else {
163         val = lookup[(unsigned char)*src++];
164         if(val == 0xff) /* bad symbol */
165           goto bad;
166         x = (x << 6) | val;
167       }
168     }
169     if(padding == 1)
170       pos[1] = (x >> 8) & 0xff;
171     pos[0] = (x >> 16) & 0xff;
172     pos += 3 - padding;
173   }
174 
175   /* Zero terminate */
176   *pos = '\0';
177 
178   /* Return the decoded data */
179   *outptr = newstr;
180   *outlen = rawlen;
181 
182   return CURLE_OK;
183 bad:
184   free(newstr);
185   return CURLE_BAD_CONTENT_ENCODING;
186 }
187 
base64_encode(const char * table64,const char * inputbuff,size_t insize,char ** outptr,size_t * outlen)188 static CURLcode base64_encode(const char *table64,
189                               const char *inputbuff, size_t insize,
190                               char **outptr, size_t *outlen)
191 {
192   char *output;
193   char *base64data;
194   const unsigned char *in = (unsigned char *)inputbuff;
195   const char *padstr = &table64[64];    /* Point to padding string. */
196 
197   *outptr = NULL;
198   *outlen = 0;
199 
200   if(!insize)
201     insize = strlen(inputbuff);
202 
203 #if SIZEOF_SIZE_T == 4
204   if(insize > UINT_MAX/4)
205     return CURLE_OUT_OF_MEMORY;
206 #endif
207 
208   base64data = output = malloc((insize + 2) / 3 * 4 + 1);
209   if(!output)
210     return CURLE_OUT_OF_MEMORY;
211 
212   while(insize >= 3) {
213     *output++ = table64[ in[0] >> 2 ];
214     *output++ = table64[ ((in[0] & 0x03) << 4) | (in[1] >> 4) ];
215     *output++ = table64[ ((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6) ];
216     *output++ = table64[ in[2] & 0x3F ];
217     insize -= 3;
218     in += 3;
219   }
220   if(insize) {
221     /* this is only one or two bytes now */
222     *output++ = table64[ in[0] >> 2 ];
223     if(insize == 1) {
224       *output++ = table64[ ((in[0] & 0x03) << 4) ];
225       if(*padstr) {
226         *output++ = *padstr;
227         *output++ = *padstr;
228       }
229     }
230     else {
231       /* insize == 2 */
232       *output++ = table64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4) ];
233       *output++ = table64[ ((in[1] & 0x0F) << 2) ];
234       if(*padstr)
235         *output++ = *padstr;
236     }
237   }
238 
239   /* Zero terminate */
240   *output = '\0';
241 
242   /* Return the pointer to the new data (allocated memory) */
243   *outptr = base64data;
244 
245   /* Return the length of the new data */
246   *outlen = (size_t)(output - base64data);
247 
248   return CURLE_OK;
249 }
250 
251 /*
252  * Curl_base64_encode()
253  *
254  * Given a pointer to an input buffer and an input size, encode it and
255  * return a pointer in *outptr to a newly allocated memory area holding
256  * encoded data. Size of encoded data is returned in variable pointed by
257  * outlen.
258  *
259  * Input length of 0 indicates input buffer holds a NUL-terminated string.
260  *
261  * Returns CURLE_OK on success, otherwise specific error code. Function
262  * output shall not be considered valid unless CURLE_OK is returned.
263  *
264  * @unittest: 1302
265  */
Curl_base64_encode(const char * inputbuff,size_t insize,char ** outptr,size_t * outlen)266 CURLcode Curl_base64_encode(const char *inputbuff, size_t insize,
267                             char **outptr, size_t *outlen)
268 {
269   return base64_encode(base64encdec, inputbuff, insize, outptr, outlen);
270 }
271 
272 /*
273  * Curl_base64url_encode()
274  *
275  * Given a pointer to an input buffer and an input size, encode it and
276  * return a pointer in *outptr to a newly allocated memory area holding
277  * encoded data. Size of encoded data is returned in variable pointed by
278  * outlen.
279  *
280  * Input length of 0 indicates input buffer holds a NUL-terminated string.
281  *
282  * Returns CURLE_OK on success, otherwise specific error code. Function
283  * output shall not be considered valid unless CURLE_OK is returned.
284  *
285  * @unittest: 1302
286  */
Curl_base64url_encode(const char * inputbuff,size_t insize,char ** outptr,size_t * outlen)287 CURLcode Curl_base64url_encode(const char *inputbuff, size_t insize,
288                                char **outptr, size_t *outlen)
289 {
290   return base64_encode(base64url, inputbuff, insize, outptr, outlen);
291 }
292 
293 #endif /* no users so disabled */
294