1 /*
2 * Copyright (c) 2000, 2001, 2002 X-Way Rights BV
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 */
19
20 /*!\file base64.c
21 * \brief Base64 encoding and decoding.
22 * \author Bob Deblier <bob.deblier@telenet.be>
23 */
24
25 #define BEECRYPT_DLL_EXPORT
26
27 #if HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include "beecrypt/base64.h"
32
33 #if HAVE_ENDIAN_H && HAVE_ASM_BYTEORDER_H
34 # include <endian.h>
35 #endif
36
37 #include "beecrypt/endianness.h"
38
39 #if HAVE_CTYPE_H
40 # include <ctype.h>
41 #endif
42
43 static const char* to_b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
44
45 /* encode 64 characters per line */
46 #define CHARS_PER_LINE 64
47
b64enc(const memchunk * chunk)48 char* b64enc(const memchunk* chunk)
49 {
50 int div = chunk->size / 3;
51 int rem = chunk->size % 3;
52 int chars = div*4 + rem + 1;
53 int newlines = (chars + CHARS_PER_LINE - 1) / CHARS_PER_LINE;
54
55 const byte* data = chunk->data;
56 char* string = (char*) malloc(chars + newlines + 1);
57
58 if (string)
59 {
60 register char* buf = string;
61
62 chars = 0;
63
64 while (div > 0)
65 {
66 buf[0] = to_b64[ (data[0] >> 2) & 0x3f];
67 buf[1] = to_b64[((data[0] << 4) & 0x30) | ((data[1] >> 4) & 0xf)];
68 buf[2] = to_b64[((data[1] << 2) & 0x3c) | ((data[2] >> 6) & 0x3)];
69 buf[3] = to_b64[ data[2] & 0x3f];
70 data += 3;
71 buf += 4;
72 div--;
73 chars += 4;
74 if (chars == CHARS_PER_LINE)
75 {
76 chars = 0;
77 *(buf++) = '\n';
78 }
79 }
80
81 switch (rem)
82 {
83 case 2:
84 buf[0] = to_b64[ (data[0] >> 2) & 0x3f];
85 buf[1] = to_b64[((data[0] << 4) & 0x30) + ((data[1] >> 4) & 0xf)];
86 buf[2] = to_b64[ (data[1] << 2) & 0x3c];
87 buf[3] = '=';
88 buf += 4;
89 chars += 4;
90 break;
91 case 1:
92 buf[0] = to_b64[ (data[0] >> 2) & 0x3f];
93 buf[1] = to_b64[ (data[0] << 4) & 0x30];
94 buf[2] = '=';
95 buf[3] = '=';
96 buf += 4;
97 chars += 4;
98 break;
99 }
100
101 /* *(buf++) = '\n'; This would result in a buffer overrun */
102 *buf = '\0';
103 }
104
105 return string;
106 }
107
b64dec(const char * string)108 memchunk* b64dec(const char* string)
109 {
110 /* return a decoded memchunk, or a null pointer in case of failure */
111
112 memchunk* rc = 0;
113
114 if (string)
115 {
116 register int length = strlen(string);
117
118 /* do a format verification first */
119 if (length > 0)
120 {
121 register int count = 0, rem = 0;
122 register const char* tmp = string;
123
124 while (length > 0)
125 {
126 register int skip = strspn(tmp, to_b64);
127 count += skip;
128 length -= skip;
129 tmp += skip;
130 if (length > 0)
131 {
132 register int i, vrfy = strcspn(tmp, to_b64);
133
134 for (i = 0; i < vrfy; i++)
135 {
136 if (isspace(tmp[i]))
137 continue;
138
139 if (tmp[i] == '=')
140 {
141 /* we should check if we're close to the end of the string */
142 rem = count % 4;
143
144 /* rem must be either 2 or 3, otherwise no '=' should be here */
145 if (rem < 2)
146 return 0;
147
148 /* end-of-message recognized */
149 break;
150 }
151 else
152 {
153 /* Transmission error; RFC tells us to ignore this, but:
154 * - the rest of the message is going to even more corrupt since we're sliding bits out of place
155 * If a message is corrupt, it should be dropped. Period.
156 */
157
158 return 0;
159 }
160 }
161
162 length -= vrfy;
163 tmp += vrfy;
164 }
165 }
166
167 rc = memchunkAlloc((count / 4) * 3 + (rem ? (rem - 1) : 0));
168
169 if (rc)
170 {
171 if (count > 0)
172 {
173 register int i, qw = 0, tw = 0;
174 register byte* data = rc->data;
175
176 length = strlen(tmp = string);
177
178 for (i = 0; i < length; i++)
179 {
180 register char ch = string[i];
181 register byte bits = 0;
182
183 if (isspace(ch))
184 continue;
185
186 if ((ch >= 'A') && (ch <= 'Z'))
187 {
188 bits = (byte) (ch - 'A');
189 }
190 else if ((ch >= 'a') && (ch <= 'z'))
191 {
192 bits = (byte) (ch - 'a' + 26);
193 }
194 else if ((ch >= '0') && (ch <= '9'))
195 {
196 bits = (byte) (ch - '0' + 52);
197 }
198 else if (ch == '+')
199 {
200 bits = 62;
201 }
202 else if (ch == '/')
203 {
204 bits = 63;
205 }
206 else if (ch == '=')
207 break;
208
209 switch (qw++)
210 {
211 case 0:
212 data[tw+0] = (bits << 2) & 0xfc;
213 break;
214 case 1:
215 data[tw+0] |= (bits >> 4) & 0x03;
216 data[tw+1] = (bits << 4) & 0xf0;
217 break;
218 case 2:
219 data[tw+1] |= (bits >> 2) & 0x0f;
220 data[tw+2] = (bits << 6) & 0xc0;
221 break;
222 case 3:
223 data[tw+2] |= bits & 0x3f;
224 break;
225 }
226
227 if (qw == 4)
228 {
229 qw = 0;
230 tw += 3;
231 }
232 }
233 }
234 }
235 }
236 }
237
238 return rc;
239 }
240
241 int b64encode_chars_per_line = B64ENCODE_CHARS_PER_LINE;
242
243 const char * b64encode_eolstr = B64ENCODE_EOLSTR;
244
b64encode(const void * data,size_t ns)245 char* b64encode(const void* data, size_t ns)
246 {
247 static char b64enc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
248 const char *e;
249 const unsigned char *s = data;
250 unsigned char *t, *te;
251 int nt;
252 int lc;
253 unsigned c;
254
255 if (s == NULL) return NULL;
256
257 if (ns == 0) ns = strlen((const char*) s);
258 nt = ((ns + 2) / 3) * 4;
259
260 /* Add additional bytes necessary for eol string(s). */
261 if (b64encode_chars_per_line > 0 && b64encode_eolstr != NULL)
262 {
263 lc = (nt + b64encode_chars_per_line - 1) / b64encode_chars_per_line;
264 if (((nt + b64encode_chars_per_line - 1) % b64encode_chars_per_line) != 0)
265 ++lc;
266 nt += lc * strlen(b64encode_eolstr);
267 }
268
269 t = te = malloc(nt + 1);
270
271 lc = 0;
272 if (te)
273 {
274 while (ns > 0)
275 {
276 c = *s++;
277 *te++ = b64enc[ (c >> 2) ], lc++;
278 *te++ = b64enc[ ((c & 0x3) << 4) | (((ns-1) > 0 ? *s : 0) >> 4) ], lc++;
279 if (--ns == 0)
280 {
281 *te++ = '=';
282 *te++ = '=';
283 continue;
284 }
285 c = *s++;
286 *te++ = b64enc[ ((c & 0xf) << 2) | (((ns-1) > 0 ? *s : 0) >> 6) ], lc++;
287 if (--ns == 0)
288 {
289 *te++ = '=';
290 continue;
291 }
292 *te++ = b64enc[ (int)(*s & 0x3f) ], lc++;
293
294 /* Append eol string if desired. */
295 if (b64encode_chars_per_line > 0 && b64encode_eolstr != NULL)
296 {
297 if (lc >= b64encode_chars_per_line)
298 {
299 for (e = b64encode_eolstr; *e != '\0'; e++)
300 *te++ = *e;
301 lc = 0;
302 }
303 }
304 s++;
305 --ns;
306 }
307
308 /* Append eol string if desired. */
309 if (b64encode_chars_per_line > 0 && b64encode_eolstr != NULL)
310 {
311 if (lc != 0)
312 {
313 for (e = b64encode_eolstr; *e != '\0'; e++)
314 *te++ = *e;
315 }
316 }
317 *te = '\0';
318 }
319
320 return (char*) t;
321 }
322
323 #define CRC24_INIT 0xb704ceL
324 #define CRC24_POLY 0x1864cfbL
325
b64crc(const unsigned char * data,size_t ns)326 char* b64crc (const unsigned char* data, size_t ns)
327 {
328 const unsigned char *s = data;
329 uint32_t crc = CRC24_INIT;
330
331 while (ns-- > 0)
332 {
333 int i;
334 crc ^= (*s++) << 16;
335 for (i = 0; i < 8; i++)
336 {
337 crc <<= 1;
338 if (crc & 0x1000000)
339 crc ^= CRC24_POLY;
340 }
341 }
342 crc &= 0xffffff;
343 #if !WORDS_BIGENDIAN
344 crc = swapu32(crc);
345 #endif
346 data = (byte *)&crc;
347 data++;
348 ns = 3;
349
350 return b64encode(data, ns);
351 }
352
353 const char* b64decode_whitespace = B64DECODE_WHITESPACE;
354
b64decode(const char * s,void ** datap,size_t * lenp)355 int b64decode(const char* s, void** datap, size_t* lenp)
356 {
357 unsigned char b64dec[256];
358 const unsigned char *t;
359 unsigned char *te;
360 int ns, nt;
361 unsigned a, b, c, d;
362
363 if (s == NULL) return 1;
364
365 /* Setup character lookup tables. */
366 memset(b64dec, 0x80, sizeof(b64dec));
367 for (c = 'A'; c <= 'Z'; c++)
368 b64dec[ c ] = 0 + (c - 'A');
369 for (c = 'a'; c <= 'z'; c++)
370 b64dec[ c ] = 26 + (c - 'a');
371 for (c = '0'; c <= '9'; c++)
372 b64dec[ c ] = 52 + (c - '0');
373 b64dec[(unsigned)'+'] = 62;
374 b64dec[(unsigned)'/'] = 63;
375 b64dec[(unsigned)'='] = 0;
376
377 /* Mark whitespace characters. */
378 if (b64decode_whitespace)
379 {
380 const char *e;
381 for (e = b64decode_whitespace; *e != '\0'; e++)
382 {
383 if (b64dec[ (unsigned)*e ] == 0x80)
384 b64dec[ (unsigned)*e ] = 0x81;
385 }
386 }
387
388 /* Validate input buffer */
389 ns = 0;
390 for (t = (unsigned char*) s; *t != '\0'; t++)
391 {
392 switch (b64dec[(unsigned) *t])
393 {
394 case 0x80: /* invalid chararcter */
395 return 3;
396 case 0x81: /* white space */
397 break;
398 default:
399 ns++;
400 break;
401 }
402 }
403
404 if (((unsigned) ns) & 0x3) return 2;
405
406 nt = (ns / 4) * 3;
407 t = te = malloc(nt + 1);
408
409 while (ns > 0)
410 {
411 /* Get next 4 characters, ignoring whitespace. */
412 while ((a = b64dec[ (unsigned)*s++ ]) == 0x81)
413 ;
414 while ((b = b64dec[ (unsigned)*s++ ]) == 0x81)
415 ;
416 while ((c = b64dec[ (unsigned)*s++ ]) == 0x81)
417 ;
418 while ((d = b64dec[ (unsigned)*s++ ]) == 0x81)
419 ;
420
421 ns -= 4;
422 *te++ = (a << 2) | (b >> 4);
423 if (s[-2] == '=') break;
424 *te++ = (b << 4) | (c >> 2);
425 if (s[-1] == '=') break;
426 *te++ = (c << 6) | d;
427 }
428
429 if (ns != 0)
430 { /* XXX can't happen, just in case */
431 if (t) free((void *)t);
432 return 1;
433 }
434 if (lenp)
435 *lenp = (te - t);
436
437 if (datap)
438 *datap = (void *)t;
439 else
440 if (t) free((void *)t);
441
442 return 0;
443 }
444