ad254595f4726afaa6230a28fee3e0545842d77f
[platform/upstream/curl.git] / lib / base64.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2016, 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.haxx.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  ***************************************************************************/
22
23 /* Base64 encoding/decoding */
24
25 #include "curl_setup.h"
26 #include "urldata.h" /* for the Curl_easy definition */
27 #include "warnless.h"
28 #include "curl_base64.h"
29 #include "non-ascii.h"
30
31 /* The last 3 #include files should be in this order */
32 #include "curl_printf.h"
33 #include "curl_memory.h"
34 #include "memdebug.h"
35
36 /* ---- Base64 Encoding/Decoding Table --- */
37 static const char base64[]=
38   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
39
40 /* The Base 64 encoding with an URL and filename safe alphabet, RFC 4648
41    section 5 */
42 static const char base64url[]=
43   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
44
45 static size_t decodeQuantum(unsigned char *dest, const char *src)
46 {
47   size_t padding = 0;
48   const char *s, *p;
49   unsigned long i, x = 0;
50
51   for(i = 0, s = src; i < 4; i++, s++) {
52     unsigned long v = 0;
53
54     if(*s == '=') {
55       x = (x << 6);
56       padding++;
57     }
58     else {
59       p = base64;
60
61       while(*p && (*p != *s)) {
62         v++;
63         p++;
64       }
65
66       if(*p == *s)
67         x = (x << 6) + v;
68       else
69         return 0;
70     }
71   }
72
73   if(padding < 1)
74     dest[2] = curlx_ultouc(x & 0xFFUL);
75
76   x >>= 8;
77   if(padding < 2)
78     dest[1] = curlx_ultouc(x & 0xFFUL);
79
80   x >>= 8;
81   dest[0] = curlx_ultouc(x & 0xFFUL);
82
83   return 3 - padding;
84 }
85
86 /*
87  * Curl_base64_decode()
88  *
89  * Given a base64 NUL-terminated string at src, decode it and return a
90  * pointer in *outptr to a newly allocated memory area holding decoded
91  * data. Size of decoded data is returned in variable pointed by outlen.
92  *
93  * Returns CURLE_OK on success, otherwise specific error code. Function
94  * output shall not be considered valid unless CURLE_OK is returned.
95  *
96  * When decoded data length is 0, returns NULL in *outptr.
97  *
98  * @unittest: 1302
99  */
100 CURLcode Curl_base64_decode(const char *src,
101                             unsigned char **outptr, size_t *outlen)
102 {
103   size_t srclen = 0;
104   size_t length = 0;
105   size_t padding = 0;
106   size_t i;
107   size_t numQuantums;
108   size_t rawlen = 0;
109   unsigned char *pos;
110   unsigned char *newstr;
111
112   *outptr = NULL;
113   *outlen = 0;
114   srclen = strlen(src);
115
116   /* Check the length of the input string is valid */
117   if(!srclen || srclen % 4)
118     return CURLE_BAD_CONTENT_ENCODING;
119
120   /* Find the position of any = padding characters */
121   while((src[length] != '=') && src[length])
122     length++;
123
124   /* A maximum of two = padding characters is allowed */
125   if(src[length] == '=') {
126     padding++;
127     if(src[length + 1] == '=')
128       padding++;
129   }
130
131   /* Check the = padding characters weren't part way through the input */
132   if(length + padding != srclen)
133     return CURLE_BAD_CONTENT_ENCODING;
134
135   /* Calculate the number of quantums */
136   numQuantums = srclen / 4;
137
138   /* Calculate the size of the decoded string */
139   rawlen = (numQuantums * 3) - padding;
140
141   /* Allocate our buffer including room for a zero terminator */
142   newstr = malloc(rawlen + 1);
143   if(!newstr)
144     return CURLE_OUT_OF_MEMORY;
145
146   pos = newstr;
147
148   /* Decode the quantums */
149   for(i = 0; i < numQuantums; i++) {
150     size_t result = decodeQuantum(pos, src);
151     if(!result) {
152       free(newstr);
153
154       return CURLE_BAD_CONTENT_ENCODING;
155     }
156
157     pos += result;
158     src += 4;
159   }
160
161   /* Zero terminate */
162   *pos = '\0';
163
164   /* Return the decoded data */
165   *outptr = newstr;
166   *outlen = rawlen;
167
168   return CURLE_OK;
169 }
170
171 static CURLcode base64_encode(const char *table64,
172                               struct Curl_easy *data,
173                               const char *inputbuff, size_t insize,
174                               char **outptr, size_t *outlen)
175 {
176   CURLcode result;
177   unsigned char ibuf[3];
178   unsigned char obuf[4];
179   int i;
180   int inputparts;
181   char *output;
182   char *base64data;
183   char *convbuf = NULL;
184
185   const char *indata = inputbuff;
186
187   *outptr = NULL;
188   *outlen = 0;
189
190   if(!insize)
191     insize = strlen(indata);
192
193   base64data = output = malloc(insize * 4 / 3 + 4);
194   if(!output)
195     return CURLE_OUT_OF_MEMORY;
196
197   /*
198    * The base64 data needs to be created using the network encoding
199    * not the host encoding.  And we can't change the actual input
200    * so we copy it to a buffer, translate it, and use that instead.
201    */
202   result = Curl_convert_clone(data, indata, insize, &convbuf);
203   if(result) {
204     free(output);
205     return result;
206   }
207
208   if(convbuf)
209     indata = (char *)convbuf;
210
211   while(insize > 0) {
212     for(i = inputparts = 0; i < 3; i++) {
213       if(insize > 0) {
214         inputparts++;
215         ibuf[i] = (unsigned char) *indata;
216         indata++;
217         insize--;
218       }
219       else
220         ibuf[i] = 0;
221     }
222
223     obuf[0] = (unsigned char)  ((ibuf[0] & 0xFC) >> 2);
224     obuf[1] = (unsigned char) (((ibuf[0] & 0x03) << 4) | \
225                                ((ibuf[1] & 0xF0) >> 4));
226     obuf[2] = (unsigned char) (((ibuf[1] & 0x0F) << 2) | \
227                                ((ibuf[2] & 0xC0) >> 6));
228     obuf[3] = (unsigned char)   (ibuf[2] & 0x3F);
229
230     switch(inputparts) {
231     case 1: /* only one byte read */
232       snprintf(output, 5, "%c%c==",
233                table64[obuf[0]],
234                table64[obuf[1]]);
235       break;
236
237     case 2: /* two bytes read */
238       snprintf(output, 5, "%c%c%c=",
239                table64[obuf[0]],
240                table64[obuf[1]],
241                table64[obuf[2]]);
242       break;
243
244     default:
245       snprintf(output, 5, "%c%c%c%c",
246                table64[obuf[0]],
247                table64[obuf[1]],
248                table64[obuf[2]],
249                table64[obuf[3]]);
250       break;
251     }
252     output += 4;
253   }
254
255   /* Zero terminate */
256   *output = '\0';
257
258   /* Return the pointer to the new data (allocated memory) */
259   *outptr = base64data;
260
261   free(convbuf);
262
263   /* Return the length of the new data */
264   *outlen = strlen(base64data);
265
266   return CURLE_OK;
267 }
268
269 /*
270  * Curl_base64_encode()
271  *
272  * Given a pointer to an input buffer and an input size, encode it and
273  * return a pointer in *outptr to a newly allocated memory area holding
274  * encoded data. Size of encoded data is returned in variable pointed by
275  * outlen.
276  *
277  * Input length of 0 indicates input buffer holds a NUL-terminated string.
278  *
279  * Returns CURLE_OK on success, otherwise specific error code. Function
280  * output shall not be considered valid unless CURLE_OK is returned.
281  *
282  * When encoded data length is 0, returns NULL in *outptr.
283  *
284  * @unittest: 1302
285  */
286 CURLcode Curl_base64_encode(struct Curl_easy *data,
287                             const char *inputbuff, size_t insize,
288                             char **outptr, size_t *outlen)
289 {
290   return base64_encode(base64, data, inputbuff, insize, outptr, outlen);
291 }
292
293 /*
294  * Curl_base64url_encode()
295  *
296  * Given a pointer to an input buffer and an input size, encode it and
297  * return a pointer in *outptr to a newly allocated memory area holding
298  * encoded data. Size of encoded data is returned in variable pointed by
299  * outlen.
300  *
301  * Input length of 0 indicates input buffer holds a NUL-terminated string.
302  *
303  * Returns CURLE_OK on success, otherwise specific error code. Function
304  * output shall not be considered valid unless CURLE_OK is returned.
305  *
306  * When encoded data length is 0, returns NULL in *outptr.
307  *
308  * @unittest: 1302
309  */
310 CURLcode Curl_base64url_encode(struct Curl_easy *data,
311                                const char *inputbuff, size_t insize,
312                                char **outptr, size_t *outlen)
313 {
314   return base64_encode(base64url, data, inputbuff, insize, outptr, outlen);
315 }