1 /*	$Id: base64.c 20800 2012-01-19 05:13:45Z m-oki $	*/
2 
3 /*
4  * Copyright (c) 2012, Internet Initiative Japan, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include "config.h"
31 
32 #include <inttypes.h>
33 #include <string.h>
34 
35 #include <time.h>
36 #include <libarms.h>
37 #include <libarms_log.h>
38 
39 #include <libarms/base64.h>
40 
41 /*
42  * base64 implementation from xmpp_util.cpp by ebisawa@.
43  */
44 static char Base64Table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
45 
46 int
arms_base64_encode(char * dest,int destmax,const char * buf,int buflen)47 arms_base64_encode(char *dest, int destmax, const char *buf, int buflen)
48 {
49 	int idx, rlen = 0;
50 
51 	while (buflen >= 3) {
52 		if (destmax < 4)
53 			return -1;
54 
55 		/* (1) */
56 		idx = (buf[0] & 0xfc) >> 2;
57 		*dest++ = Base64Table[idx];
58 
59 		/* (2) */
60 		idx = ((buf[0] & 0x03) << 4) | ((buf[1] & 0xf0) >> 4);
61 		*dest++ = Base64Table[idx];
62 
63 		/* (3) */
64 		idx = ((buf[1] & 0x0f) << 2) | ((buf[2] & 0xc0) >> 6);
65 		*dest++ = Base64Table[idx];
66 
67 		/* (4) */
68 		idx = (buf[2] & 0x3f);
69 		*dest++ = Base64Table[idx];
70 
71 		rlen += 4;
72 		destmax -= 4;
73 		buflen -= 3;
74 		buf += 3;
75 	}
76 
77 	/* the final quantum */
78 	if (buflen >= 2) {
79 		if (destmax < 4)
80 			return -1;
81 
82 		/* (1) */
83 		idx = (buf[0] & 0xfc) >> 2;
84 		*dest++ = Base64Table[idx];
85 
86 		/* (2) */
87 		idx = ((buf[0] & 0x03) << 4) | ((buf[1] & 0xf0) >> 4);
88 		*dest++ = Base64Table[idx];
89 
90 		/* (3) */
91 		idx = ((buf[1] & 0x0f) << 2);
92 		*dest++ = Base64Table[idx];
93 
94 		*dest++ = '=';
95 
96 		rlen += 4;
97 	} else if (buflen >= 1) {
98 		if (destmax < 4)
99 			return -1;
100 
101 		/* (1) */
102 		idx = (buf[0] & 0xfc) >> 2;
103 		*dest++ = Base64Table[idx];
104 
105 		/* (2) */
106 		idx = ((buf[0] & 0x03) << 4);
107 		*dest++ = Base64Table[idx];
108 
109 		*dest++ = '=';
110 		*dest++ = '=';
111 
112 		rlen += 4;
113 	}
114 
115 	return rlen;
116 }
117 
118 static char modbuf[4];
119 static int modlen;
120 
121 static void
arms_base64_reset_state(void)122 arms_base64_reset_state(void)
123 {
124 	/* note: need to keep modbuf. */
125 	modlen = 0;
126 }
127 
128 int
arms_base64_decode_stream(arms_base64_stream_t * obj,char * dest,int destmax,const char * buf,int buflen)129 arms_base64_decode_stream(arms_base64_stream_t *obj, char *dest, int destmax,
130 			  const char *buf, int buflen)
131 {
132 	int len, rlen = 0;
133 
134 	/* using like streaming. decode previous modulo and current buf */
135 	while (obj->modlen > 0) {
136 		int cplen;
137 
138 		/* to decode, at least need to read 4 byte data. */
139 		if (obj->modlen + buflen < sizeof(obj->modbuf)) {
140 			memcpy(&obj->modbuf[obj->modlen], buf, buflen);
141 			obj->modlen += buflen;
142 			return 0;
143 		}
144 		/* fill modbuf */
145 		cplen = sizeof(obj->modbuf) - obj->modlen;
146 		memcpy(&obj->modbuf[obj->modlen], buf, cplen);
147 		buf += cplen;
148 		buflen -= cplen;
149 		/* decode 4 bytes */
150 		len = arms_base64_decode(dest, destmax, obj->modbuf, 4);
151 		if (len < 0) {
152 			arms_base64_reset_state();
153 			return -1;
154 		}
155 		/*
156 		 * 1: len == 0 and modlen > 0
157 		 * 2: len > 0 and modlen == 0
158 		 */
159 		if (len > 0) {
160 			rlen += len;
161 			dest += len;
162 			destmax -= len;
163 			break;
164 		}
165 		memcpy(obj->modbuf, modbuf, sizeof(obj->modbuf));
166 		obj->modlen = modlen;
167 	}
168 
169 	len = arms_base64_decode(dest, destmax, buf, buflen);
170 	if (len < 0) {
171 		arms_base64_reset_state();
172 		return -1;
173 	}
174 	memcpy(obj->modbuf, modbuf, sizeof(obj->modbuf));
175 	obj->modlen = modlen;
176 
177 	return rlen + len;
178 }
179 
180 int
arms_base64_decode(char * dest,int destmax,const char * buf,int buflen)181 arms_base64_decode(char *dest, int destmax, const char *buf, int buflen)
182 {
183 	char *p;
184 	int idx, rlen = 0;
185 
186 	arms_base64_reset_state();
187 
188 	while (buflen >= 4) {
189 		if (destmax < 3) {
190 			libarms_log(ARMS_LOG_DEBUG,
191 			    "base64: no space available");
192 			return -1;
193 		}
194 
195 		if (buf[0] == '\r' || buf[0] == '\n') {
196 			buf++;
197 			buflen--;
198 			continue;
199 		}
200 		/* (1) */
201 		if ((p = strchr(Base64Table, buf[0])) == NULL) {
202 			libarms_log(ARMS_LOG_DEBUG,
203 			    "base64: invalid char 0x%x", buf[0]);
204 			return -1;
205 		}
206 
207 		idx = (int) (p - Base64Table);
208 		dest[0] = idx << 2;
209 
210 		/* (2) */
211 		while (buf[1] == '\r' || buf[1] == '\n') {
212 			buf++;
213 			buflen--;
214 			if (buflen < 4) {
215 				libarms_log(ARMS_LOG_DEBUG,
216 				    "base64: invalid input data");
217 				return -1;
218 			}
219 		}
220 		if ((p = strchr(Base64Table, buf[1])) == NULL) {
221 			libarms_log(ARMS_LOG_DEBUG,
222 				    "base64: invalid char 0x%x", buf[1]);
223 			return -1;
224 		}
225 
226 		idx = (int) (p - Base64Table);
227 		dest[0] |= idx >> 4;
228 		dest[1]  = (idx << 4) & 0xf0;
229 
230 		/* (3) */
231 		while (buf[2] == '\r' || buf[2] == '\n') {
232 			buf++;
233 			buflen--;
234 			if (buflen < 4) {
235 				libarms_log(ARMS_LOG_DEBUG,
236 				    "base64: invalid input data");
237 				return -1;
238 			}
239 		}
240 		if (buf[2] != '=') {
241 			if ((p = strchr(Base64Table, buf[2])) == NULL) {
242 				libarms_log(ARMS_LOG_DEBUG,
243 				    "base64: invalid char 0x%x", buf[2]);
244 				return -1;
245 			}
246 
247 			idx = (int) (p - Base64Table);
248 			dest[1] |= idx >> 2;
249 			dest[2]  = (idx << 6) & 0xc0;
250 		} else {
251 			rlen += 1;
252 			buflen = 0;
253 			break;
254 		}
255 
256 		/* (4) */
257 		while (buf[3] == '\r' || buf[3] == '\n') {
258 			buf++;
259 			buflen--;
260 			if (buflen < 4) {
261 				libarms_log(ARMS_LOG_DEBUG,
262 				    "base64: invalid input data");
263 				return -1;
264 			}
265 		}
266 		if (buf[3] != '=') {
267 			if ((p = strchr(Base64Table, buf[3])) == NULL) {
268 				libarms_log(ARMS_LOG_DEBUG,
269 				    "base64: invalid char 0x%x", buf[3]);
270 				return -1;
271 			}
272 
273 			idx = (int) (p - Base64Table);
274 			dest[2] |= idx & 0x3f;
275 		} else {
276 			rlen += 2;
277 			buflen = 0;
278 			break;
279 		}
280 
281 		rlen += 3;
282 		buflen -= 4;
283 		buf += 4;
284 		destmax -= 3;
285 		dest += 3;
286 	}
287 
288 	/* modulo data is copied to static buffer */
289 	modlen = buflen;
290 	memcpy(modbuf, buf, modlen);
291 
292 	return rlen;
293 }
294