Main Page Modules Data Structures File List Data Fields Globals Related Pages

# beecrypt/base64.c

Go to the documentation of this file.

    00001
    00007 /*
    00008  * Copyright (c) 2000-2001 Virtual Unlimited B.V.
    00009  *
    00010  * Author: Bob Deblier <bob@virtualunlimited.com>
    00011  *
    00012  * This library is free software; you can redistribute it and/or
    00013  * modify it under the terms of the GNU Lesser General Public
    00014  * License as published by the Free Software Foundation; either
    00015  * version 2.1 of the License, or (at your option) any later version.
    00016  *
    00017  * This library is distributed in the hope that it will be useful,
    00018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    00019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00020  * Lesser General Public License for more details.
    00021  *
    00022  * You should have received a copy of the GNU Lesser General Public
    00023  * License along with this library; if not, write to the Free Software
    00024  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    00025  *
    00026  */
    00027
    00028 #include "system.h"
    00029 #include "base64.h"
    00030 #include "debug.h"
    00031
    00032 /*@unchecked@*/
    00033 static int _debug = 0;
    00034
    00035 /*@unchecked@*/ /*@observer@*/
    00036 static const char* to_b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    00037
    00038 /* encode 64 characters per line */
    00039 #define CHARS_PER_LINE  64
    00040
    00041 /*@-boundswrite@*/
    00042 char* b64enc(const memchunk* chunk)
    00043 {
    00044         int div = chunk->size / 3;
    00045         int rem = chunk->size % 3;
    00046         int chars = div*4 + rem + 1;
    00047         int newlines = (chars + CHARS_PER_LINE - 1) / CHARS_PER_LINE;
    00048
    00049         const byte* data = chunk->data;
    00050         char* string = (char*) malloc(chars + newlines + 1);
    00051
    00052         if (string)
    00053         {
    00054                 register char* buf = string;
    00055
    00056                 chars = 0;
    00057
    00058                 while (div > 0)
    00059                 {
    00060                         buf[0] = to_b64[ ((unsigned)data[0] >> 2) & 0x3f];
    00061                         buf[1] = to_b64[(((unsigned)data[0] << 4) & 0x30) | (((unsigned)data[1] >> 4) & 0xf)];
    00062                         buf[2] = to_b64[(((unsigned)data[1] << 2) & 0x3c) | (((unsigned)data[2] >> 6) & 0x3)];
    00063                         buf[3] = to_b64[  (unsigned)data[2] & 0x3f];
    00064                         data += 3;
    00065                         buf += 4;
    00066                         div--;
    00067                         chars += 4;
    00068                         if (chars == CHARS_PER_LINE)
    00069                         {
    00070                                 chars = 0;
    00071                                 *(buf++) = '\n';
    00072                         }
    00073                 }
    00074
    00075                 switch (rem)
    00076                 {
    00077                 case 2:
    00078                         buf[0] = to_b64[ ((unsigned)data[0] >> 2) & 0x3f];
    00079                         buf[1] = to_b64[(((unsigned)data[0] << 4) & 0x30) + (((unsigned)data[1] >> 4) & 0xf)];
    00080                         buf[2] = to_b64[ ((unsigned)data[1] << 2) & 0x3c];
    00081                         buf[3] = '=';
    00082                         buf += 4;
    00083                         chars += 4;
    00084                         break;
    00085                 case 1:
    00086                         buf[0] = to_b64[ ((unsigned)data[0] >> 2) & 0x3f];
    00087                         buf[1] = to_b64[ ((unsigned)data[0] << 4) & 0x30];
    00088                         buf[2] = '=';
    00089                         buf[3] = '=';
    00090                         buf += 4;
    00091                         chars += 4;
    00092                         break;
    00093                 }
    00094
    00095         /*      *(buf++) = '\n'; This would result in a buffer overrun */
    00096                 *buf = '\0';
    00097         }
    00098
    00099         /*@-dependenttrans@*/ /* FIX: buf = string makes string dependent */
    00100         return string;
    00101         /*@=dependenttrans@*/
    00102 }
    00103 /*@=boundswrite@*/
    00104
    00105 /*@-boundswrite@*/
    00106 memchunk* b64dec(const char* string)
    00107 {
    00108         /* return a decoded memchunk, or a null pointer in case of failure */
    00109
    00110         memchunk* rc = 0;
    00111
    00112         if (string)
    00113         {
    00114                 register int length = strlen(string);
    00115
    00116                 /* do a format verification first */
    00117                 if (length > 0)
    00118                 {
    00119                         register int count = 0, rem = 0;
    00120                         register const char* tmp = string;
    00121
    00122                         while (length > 0)
    00123                         {
    00124                                 register int skip = strspn(tmp, to_b64);
    00125                                 count += skip;
    00126                                 length -= skip;
    00127                                 tmp += skip;
    00128                                 if (length > 0)
    00129                                 {
    00130                                         register int i, vrfy = strcspn(tmp, to_b64);
    00131
    00132                                         for (i = 0; i < vrfy; i++)
    00133                                         {
    00134                                                 if (isspace(tmp[i]))
    00135                                                         /*@innercontinue@*/ continue;
    00136
    00137                                                 if (tmp[i] == '=')
    00138                                                 {
    00139                                                         /* we should check if we're close to the end of the string */
    00140                                                         rem = count % 4;
    00141
    00142                                                         /* rem must be either 2 or 3, otherwise no '=' should be here */
    00143                                                         if (rem < 2)
    00144                                                                 return 0;
    00145
    00146                                                         /* end-of-message recognized */
    00147                                                         /*@innerbreak@*/ break;
    00148                                                 }
    00149                                                 else
    00150                                                 {
    00151                                                         /* Transmission error; RFC tells us to ignore this, but:
    00152                                                          *  - the rest of the message is going to even more corrupt since we're sliding bits out of place
    00153                                                          * If a message is corrupt, it should be dropped. Period.
    00154                                                          */
    00155
    00156                                                         return 0;
    00157                                                 }
    00158                                         }
    00159
    00160                                         length -= vrfy;
    00161                                         tmp += vrfy;
    00162                                 }
    00163                         }
    00164
    00165                         rc = memchunkAlloc((count / 4) * 3 + (rem ? (rem - 1) : 0));
    00166
    00167                         if (rc)
    00168                         {
    00169                                 if (count > 0)
    00170                                 {
    00171                                         register int i, qw = 0, tw = 0;
    00172                                         register byte* data = rc->data;
    00173
    00174                                         length = strlen(tmp = string);
    00175
    00176                                         for (i = 0; i < length; i++)
    00177                                         {
    00178                                                 register char ch = string[i];
    00179                                                 register byte bits;
    00180
    00181                                                 if (isspace(ch))
    00182                                                         continue;
    00183
    00184                                                 bits = 0;
    00185                                                 if ((ch >= 'A') && (ch <= 'Z'))
    00186                                                 {
    00187                                                         bits = (byte) (ch - 'A');
    00188                                                 }
    00189                                                 else if ((ch >= 'a') && (ch <= 'z'))
    00190                                                 {
    00191                                                         bits = (byte) (ch - 'a' + 26);
    00192                                                 }
    00193                                                 else if ((ch >= '0') && (ch <= '9'))
    00194                                                 {
    00195                                                         bits = (byte) (ch - '0' + 52);
    00196                                                 }
    00197                                                 else if (ch == '+')
    00198                                                 {
    00199                                                         bits = 62;
    00200                                                 }
    00201                                                 else if (ch == '/')
    00202                                                 {
    00203                                                         bits = 63;
    00204                                                 }
    00205                                                 else if (ch == '=')
    00206                                                         break;
    00207                                                 else
    00208                                                         {};
    00209
    00210                                                 switch (qw++)
    00211                                                 {
    00212                                                 case 0:
    00213                                                         data[tw+0] = (bits << 2) & 0xfc;
    00214                                                         /*@switchbreak@*/ break;
    00215                                                 case 1:
    00216                                                         data[tw+0] |= (bits >> 4) & 0x03;
    00217                                                         data[tw+1] = (bits << 4) & 0xf0;
    00218                                                         /*@switchbreak@*/ break;
    00219                                                 case 2:
    00220                                                         data[tw+1] |= (bits >> 2) & 0x0f;
    00221                                                         data[tw+2] = (bits << 6) & 0xc0;
    00222                                                         /*@switchbreak@*/ break;
    00223                                                 case 3:
    00224                                                         data[tw+2] |= bits & 0x3f;
    00225                                                         /*@switchbreak@*/ break;
    00226                                                 }
    00227
    00228                                                 if (qw == 4)
    00229                                                 {
    00230                                                         qw = 0;
    00231                                                         tw += 3;
    00232                                                 }
    00233                                         }
    00234                                 }
    00235                         }
    00236                 }
    00237         }
    00238
    00239         return rc;
    00240 }
    00241 /*@=boundswrite@*/
    00242
    00243 int b64encode_chars_per_line = B64ENCODE_CHARS_PER_LINE;
    00244
    00245 const char * b64encode_eolstr = B64ENCODE_EOLSTR;
    00246
    00247 /*@-boundswrite@*/
    00248 /*@-internalglobs -modfilesys @*/
    00249 char * b64encode (const void * data, int ns)
    00250 {
    00251     static char b64enc[] =
    00252         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    00253     const char *e;
    00254     const unsigned char *s = data;
    00255     unsigned char *t, *te;
    00256     int nt;
    00257     int lc;
    00258     unsigned c;
    00259
    00260     if (s == NULL)      return NULL;
    00261     if (*s == '\0')     return calloc(1, sizeof(*t));
    00262
    00263     if (ns == 0) ns = strlen(s);
    00264     nt = ((ns + 2) / 3) * 4;
    00265
    00266     /* Add additional bytes necessary for eol string(s). */
    00267     if (b64encode_chars_per_line > 0 && b64encode_eolstr != NULL) {
    00268         lc = (nt + b64encode_chars_per_line - 1) / b64encode_chars_per_line;
    00269         if (((nt + b64encode_chars_per_line - 1) % b64encode_chars_per_line) != 0)
    00270             ++lc;
    00271         nt += lc * strlen(b64encode_eolstr);
    00272     }
    00273
    00274     t = te = malloc(nt + 1);
    00275
    00276     lc = 0;
    00277     if (te)
    00278     while (ns) {
    00279
    00280 if (_debug)
    00281 fprintf(stderr, "%7u %02x %02x %02x -> %02x %02x %02x %02x\n",
    00282 (unsigned)ns, (unsigned)s[0], (unsigned)s[1], (unsigned)s[2],
    00283 (unsigned)(s[0] >> 2),
    00284 (unsigned)((s[0] & 0x3) << 4) | (s[1] >> 4),
    00285 (unsigned)((s[1] & 0xf) << 2) | (s[2] >> 6),
    00286 (unsigned)(s[2]& 0x3f));
    00287         c = *s++;
    00288         *te++ = b64enc[ (c >> 2) ], lc++;
    00289         *te++ = b64enc[ ((c & 0x3) << 4) | (*s >> 4) ], lc++;
    00290         if (--ns == 0) {
    00291             *te++ = '=';
    00292             *te++ = '=';
    00293             continue;
    00294         }
    00295         c = *s++;
    00296         *te++ = b64enc[ ((c & 0xf) << 2) | (*s >> 6) ], lc++;
    00297         if (--ns == 0) {
    00298             *te++ = '=';
    00299             continue;
    00300         }
    00301         *te++ = b64enc[ (int)(*s & 0x3f) ], lc++;
    00302
    00303         /* Append eol string if desired. */
    00304         if (b64encode_chars_per_line > 0 && b64encode_eolstr != NULL) {
    00305             if (lc >= b64encode_chars_per_line) {
    00306                 for (e = b64encode_eolstr; *e != '\0'; e++)
    00307                     *te++ = *e;
    00308                 lc = 0;
    00309             }
    00310         }
    00311         s++;
    00312         --ns;
    00313     }
    00314
    00315     if (te) {
    00316         /* Append eol string if desired. */
    00317         if (b64encode_chars_per_line > 0 && b64encode_eolstr != NULL) {
    00318             if (lc != 0) {
    00319                 for (e = b64encode_eolstr; *e != '\0'; e++)
    00320                     *te++ = *e;
    00321             }
    00322         }
    00323         *te = '\0';
    00324     }
    00325
    00326     /*@-mustfree -compdef @*/
    00327     return t;
    00328     /*@=mustfree =compdef @*/
    00329 }
    00330 /*@=globs =internalglobs =modfilesys @*/
    00331 /*@=boundswrite@*/
    00332
    00333 /*@-internalglobs -modfilesys @*/
    00334 #define CRC24_INIT 0xb704ceL
    00335 #define CRC24_POLY 0x1864cfbL
    00336
    00337 /*@-boundsread@*/
    00338 char * b64crc (const unsigned char * data, int ns)
    00339 {
    00340     const unsigned char *s = data;
    00341     uint32 crc = CRC24_INIT;
    00342
    00343     while (ns-- > 0) {
    00344         int i;
    00345         crc ^= (*s++) << 16;
    00346         for (i = 0; i < 8; i++) {
    00347             crc <<= 1;
    00348             if (crc & 0x1000000)
    00349                 crc ^= CRC24_POLY;
    00350         }
    00351     }
    00352     crc &= 0xffffff;
    00353     /*@-unrecog@*/ /* FIX: include endianness.h? */
    00354     #if !WORDS_BIGENDIAN
    00355     crc = swapu32(crc);
    00356     #endif
    00357     /*@=unrecog@*/
    00358     data = (byte *)&crc;
    00359     data++;
    00360     ns = 3;
    00361     return b64encode(data, ns);
    00362 }
    00363 /*@=boundsread@*/
    00364 /*@=internalglobs =modfilesys @*/
    00365
    00366 const char * b64decode_whitespace = B64DECODE_WHITESPACE;
    00367
    00368 /*@-internalglobs -modfilesys @*/
    00369 /*@-boundswrite@*/
    00370 int b64decode (const char * s, void ** datap, int *lenp)
    00371 {
    00372     unsigned char b64dec[256];
    00373     const unsigned char *t;
    00374     unsigned char *te;
    00375     int ns, nt;
    00376     unsigned a, b, c, d;
    00377
    00378     if (s == NULL)      return 1;
    00379
    00380     /* Setup character lookup tables. */
    00381     memset(b64dec, 0x80, sizeof(b64dec));
    00382     for (c = 'A'; c <= 'Z'; c++)
    00383         b64dec[ c ] = 0 + (c - 'A');
    00384     for (c = 'a'; c <= 'z'; c++)
    00385         b64dec[ c ] = 26 + (c - 'a');
    00386     for (c = '0'; c <= '9'; c++)
    00387         b64dec[ c ] = 52 + (c - '0');
    00388     b64dec[(unsigned)'+'] = 62;
    00389     b64dec[(unsigned)'/'] = 63;
    00390     b64dec[(unsigned)'='] = 0;
    00391
    00392     /* Mark whitespace characters. */
    00393     if (b64decode_whitespace) {
    00394         const char *e;
    00395         for (e = b64decode_whitespace; *e != '\0'; e++) {
    00396             if (b64dec[ (unsigned)*e ] == 0x80)
    00397                 b64dec[ (unsigned)*e ] = 0x81;
    00398         }
    00399     }
    00400
    00401     /* Validate input buffer */
    00402     ns = 0;
    00403     for (t = s; *t != '\0'; t++) {
    00404         switch (b64dec[ (unsigned)*t ]) {
    00405         case 0x80:      /* invalid chararcter */
    00406 if (_debug)
    00407 fprintf(stderr, "--- b64decode %c(%02x) %02x\n", *t, (unsigned)(*t & 0xff), (unsigned)b64dec[ (unsigned)*t ]);
    00408             return 3;
    00409             /*@notreached@*/ /*@switchbreak@*/ break;
    00410         case 0x81:      /* white space */
    00411             /*@switchbreak@*/ break;
    00412         default:
    00413             ns++;
    00414             /*@switchbreak@*/ break;
    00415         }
    00416     }
    00417
    00418     if (ns & 0x3)       return 2;
    00419
    00420     nt = (ns / 4) * 3;
    00421     t = te = malloc(nt + 1);
    00422
    00423     while (ns > 0) {
    00424
    00425         /* Get next 4 characters, ignoring whitespace. */
    00426         while ((a = b64dec[ (unsigned)*s++ ]) == 0x81)
    00427             {};
    00428         while ((b = b64dec[ (unsigned)*s++ ]) == 0x81)
    00429             {};
    00430         while ((c = b64dec[ (unsigned)*s++ ]) == 0x81)
    00431             {};
    00432         while ((d = b64dec[ (unsigned)*s++ ]) == 0x81)
    00433             {};
    00434
    00435 if (_debug)
    00436 fprintf(stderr, "%7u %02x %02x %02x %02x -> %02x %02x %02x\n",
    00437 (unsigned)ns, a, b, c, d,
    00438 (((a << 2) | (b >> 4)) & 0xff),
    00439 (((b << 4) | (c >> 2)) & 0xff),
    00440 (((c << 6) | d) & 0xff));
    00441
    00442         ns -= 4;
    00443         *te++ = (a << 2) | (b >> 4);
    00444         if (s[-2] == '=') break;
    00445         *te++ = (b << 4) | (c >> 2);
    00446         if (s[-1] == '=') break;
    00447         *te++ = (c << 6) | d;
    00448     }
    00449
    00450     if (ns != 0) {              /* XXX can't happen, just in case */
    00451         if (t) free((void *)t);
    00452         return 1;
    00453     }
    00454     if (lenp)
    00455         *lenp = (te - t);
    00456
    00457     if (datap)
    00458         *datap = (void *)t;
    00459     else
    00460         if (t) free((void *)t);
    00461     else
    00462         {};
    00463
    00464     return 0;
    00465 }
    00466 /*@=boundswrite@*/
    00467 /*@=globs =internalglobs =modfilesys @*/