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