X-Git-Url: https://git.toastfreeware.priv.at/debian/iodine.git/blobdiff_plain/4d3b42514067319b3fa92d91a20df6802dca0d17..81f2f4b5b93cecc84dcf6f3c80723baefc0a07aa:/src/base64.c diff --git a/src/base64.c b/src/base64.c index 512a199..71bbcf1 100644 --- a/src/base64.c +++ b/src/base64.c @@ -1,5 +1,7 @@ /* - * Copyright (c) 2006-2007 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson + * Mostly rewritten 2009 J.A.Bezemer@opensourcepartners.nl * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -19,26 +21,33 @@ #include #include "encoding.h" -#include "common.h" #include "base64.h" -static const char cb64[] = - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789."; -static unsigned char rev64[128]; +#define BLKSIZE_RAW 3 +#define BLKSIZE_ENC 4 + +/* Note: the "unofficial" char is last here, which means that the \377 pattern + in DOWNCODECCHECK1 ('Y' request) will properly test it. */ +static const char cb64[] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789+"; +static unsigned char rev64[256]; static int reverse_init = 0; -#define REV64(x) rev64[(int) (x)] -#define MODE (cb64[62]) -#define P62 (cb64[62]) -#define P63 (cb64[63]) +static int base64_encode(char *, size_t *, const void *, size_t); +static int base64_decode(void *, size_t *, const char *, size_t); +static int base64_handles_dots(); +static int base64_blksize_raw(); +static int base64_blksize_enc(); static struct encoder base64_encoder = { - "BASE64", + "Base64", base64_encode, base64_decode, base64_handles_dots, - base64_handles_dots + base64_handles_dots, + base64_blksize_raw, + base64_blksize_enc }; struct encoder @@ -47,292 +56,151 @@ struct encoder return &base64_encoder; } -int +static int base64_handles_dots() { return 0; } -static void -findesc(int *count, unsigned char *esc, char c1, char c2, char c3, char c4) +static int +base64_blksize_raw() { - int min1 = 0; - int min2 = 0; - - int num1 = 0xFF; /* a very big number */ - int num2 = 0xFE; /* a nearly as big number */ - - int i; - - /* check if no more escapes needed */ - if (count[62] == 0 && count[63] == 0) { - esc[0] = MODE; - esc[1] = MODE; - return; - } - - for (i = 0; i < 62; i++) { - if (i == c1 || i == c2 || i == c3 || i == c4) { - continue; - } - - if (count[i] < num1) { - min2 = min1; - num2 = num1; - min1 = i; - num1 = count[i]; - } else if (count[i] < num2) { - min2 = i; - num2 = count[i]; - } - } - - esc[0] = cb64[min1]; - esc[1] = cb64[min2]; + return BLKSIZE_RAW; } - -static void -escape_chars(char *buf, size_t buflen) -{ - int counter[64]; - int escapes; - int reset; - int i; - unsigned char temp[4096]; - unsigned char *r; - unsigned char *w; - unsigned char *e; - unsigned char esc[2]; - - memset(counter, 0, sizeof(counter)); - esc[0] = P62; - esc[1] = P63; - - /* first, find the number of times each token is used */ - r = (unsigned char *) buf; - w = temp; - while (*r) { - counter[REV64(*r)]++; - *w++ = *r++; - } - /* check if work needed */ - if (counter[62] == 0 && counter[63] == 0) - return; - - r = temp; - w = (unsigned char *) buf; - reset = 1; - escapes = 0; - /* check a block for esc chars */ - while (*r) { - if (reset == 0 && escapes == 0 && ( - r[0] == esc[0] || r[1] == esc[0] ||r[2] == esc[0] ||r[2] == esc[0] || - r[0] == esc[1] || r[1] == esc[1] ||r[2] == esc[1] ||r[2] == esc[1])) { - /* last set of escape chars were unused. - * if we reset last escape switch then maybe we dont have to switch now */ - - /* change the latest escape switch to 999 (RESET) */ - e[1] = MODE; - e[2] = MODE; - - /* store default esc chars */ - esc[0] = P62; - esc[1] = P63; - - reset = 1; - } - /* these two if blocks can not be combined because a block can contain both - * char 9 and/or . and the current escape chars. */ - if (r[0] == esc[0] || r[1] == esc[0] ||r[2] == esc[0] ||r[2] == esc[0] || - r[0] == esc[1] || r[1] == esc[1] ||r[2] == esc[1] ||r[2] == esc[1]) { - /* switch escape chars */ - escapes = 0; - reset = 0; - - /* find 2 suitable escape chars */ - findesc(counter, esc, REV64(r[0]), REV64(r[1]), REV64(r[2]), REV64(r[3])); - - /* store escape switch position */ - e = w; - - /* write new escape chars */ - *w++ = MODE; - *w++ = esc[0]; - *w++ = esc[1]; - } - - /* update counter on remaining chars */ - for (i = 0; i < 4; i++) { - if (r[i]) - counter[REV64(r[i])]--; - } - - /* do the escaping */ - for (i = 0; i < 4; i++) { - if (r[i] == P62) { - r[i] = esc[0]; - escapes++; - } else if (r[i] == P63) { - r[i] = esc[1]; - escapes++; - } - } - - /* copy back to buf */ - *w++ = *r++; - *w++ = *r++; - *w++ = *r++; - *w++ = *r++; - } +static int +base64_blksize_enc() +{ + return BLKSIZE_ENC; } -int -base64_encode(char *buf, size_t *buflen, const void *data, size_t size) +inline static void +base64_reverse_init() { - size_t newsize; - size_t maxsize; - unsigned char c; - unsigned char *s; - unsigned char *p; - unsigned char *q; int i; + unsigned char c; - memset(buf, 0, *buflen); - if (!reverse_init) { + memset (rev64, 0, 256); for (i = 0; i < 64; i++) { c = cb64[i]; rev64[(int) c] = i; } reverse_init = 1; } +} - /* how many chars can we encode within the buf */ - maxsize = 3 * (*buflen / 4 - 1) - 1; - /* how big will the encoded data be */ - newsize = 4 * (size / 3 + 1) + 1; - /* if the buffer is too small, eat some of the data */ - if (*buflen < newsize) { - size = maxsize; - } +static int +base64_encode(char *buf, size_t *buflen, const void *data, size_t size) +/* + * Fills *buf with max. *buflen characters, encoding size bytes of *data. + * + * NOTE: *buf space should be at least 1 byte _more_ than *buflen + * to hold the trailing '\0'. + * + * return value : #bytes filled in buf (excluding \0) + * sets *buflen to : #bytes encoded from data + */ +{ + unsigned char *udata = (unsigned char *) data; + int iout = 0; /* to-be-filled output char */ + int iin = 0; /* one more than last input byte that can be + successfully decoded */ - p = s = (unsigned char *) buf; - q = (unsigned char *)data; + /* Note: Don't bother to optimize manually. GCC optimizes + better(!) when using simplistic array indexing. */ - for(i=0;i> 2)]; - p[1] = cb64[(((q[0] & 0x03) << 4) | ((q[1] & 0xf0) >> 4))]; - p[2] = (i+1 < size) ? cb64[((q[1] & 0x0f) << 2 ) | ((q[2] & 0xc0) >> 6)] : '\0'; - p[3] = (i+2 < size) ? cb64[(q[2] & 0x3f)] : '\0'; - - q += 3; - p += 4; - } - *p = 0; + while (1) { + if (iout >= *buflen || iin >= size) + break; + buf[iout] = cb64[((udata[iin] & 0xfc) >> 2)]; + iout++; - escape_chars(buf, *buflen); + if (iout >= *buflen || iin >= size) { + iout--; /* previous char is useless */ + break; + } + buf[iout] = cb64[((udata[iin] & 0x03) << 4) | + ((iin + 1 < size) ? + ((udata[iin + 1] & 0xf0) >> 4) : 0)]; + iin++; /* 0 complete, iin=1 */ + iout++; + + if (iout >= *buflen || iin >= size) + break; + buf[iout] = cb64[((udata[iin] & 0x0f) << 2 ) | + ((iin + 1 < size) ? + ((udata[iin + 1] & 0xc0) >> 6) : 0)]; + iin++; /* 1 complete, iin=2 */ + iout++; + + if (iout >= *buflen || iin >= size) + break; + buf[iout] = cb64[(udata[iin] & 0x3f)]; + iin++; /* 2 complete, iin=3 */ + iout++; + } + + buf[iout] = '\0'; /* store number of bytes from data that was used */ - *buflen = size; + *buflen = iin; - return strlen(buf) - 1; + return iout; } -#define DECODE_ERROR 0xffffffff +#define REV64(x) rev64[(int) (x)] static int -decode_token(const unsigned char *t, unsigned char *data, size_t len) +base64_decode(void *buf, size_t *buflen, const char *str, size_t slen) +/* + * Fills *buf with max. *buflen bytes, decoded from slen chars in *str. + * Decoding stops early when *str contains \0. + * Illegal encoded chars are assumed to decode to zero. + * + * NOTE: *buf space should be at least 1 byte _more_ than *buflen + * to hold a trailing '\0' that is added (though *buf will usually + * contain full-binary data). + * + * return value : #bytes filled in buf (excluding \0) + */ { - if (len < 2) - return 0; - - data[0] = ((REV64(t[0]) & 0x3f) << 2) | - ((REV64(t[1]) & 0x30) >> 4); - - if (len < 3) - return 1; - - data[1] = ((REV64(t[1]) & 0x0f) << 4) | - ((REV64(t[2]) & 0x3c) >> 2); + unsigned char *ubuf = (unsigned char *) buf; + int iout = 0; /* to-be-filled output byte */ + int iin = 0; /* next input char to use in decoding */ - if (len < 4) - return 2; + base64_reverse_init (); - data[2] = ((REV64(t[2]) & 0x03) << 6) | - (REV64(t[3]) & 0x3f); + /* Note: Don't bother to optimize manually. GCC optimizes + better(!) when using simplistic array indexing. */ - return 3; -} - -int -base64_decode(void *buf, size_t *buflen, const char *str, size_t slen) -{ - unsigned char *q; - size_t newsize; - size_t maxsize; - const char *p; - unsigned char c; - unsigned char block[4]; - unsigned char prot62; - unsigned char prot63; - int len; - int i; + while (1) { + if (iout >= *buflen || iin + 1 >= slen || + str[iin] == '\0' || str[iin + 1] == '\0') + break; + ubuf[iout] = ((REV64(str[iin]) & 0x3f) << 2) | + ((REV64(str[iin + 1]) & 0x30) >> 4); + iin++; /* 0 used up, iin=1 */ + iout++; - if (!reverse_init) { - for (i = 0; i < 64; i++) { - c = cb64[i]; - rev64[(int) c] = i; - } - reverse_init = 1; - } - - /* chars needed to decode slen */ - newsize = 3 * (slen / 4 + 1) + 1; - /* encoded chars that fit in buf */ - maxsize = 4 * (*buflen / 3 + 1) + 1; - /* if the buffer is too small, eat some of the data */ - if (*buflen < newsize) { - slen = maxsize; - } - - prot62 = P62; - prot63 = P63; + if (iout >= *buflen || iin + 1 >= slen || + str[iin] == '\0' || str[iin + 1] == '\0') + break; + ubuf[iout] = ((REV64(str[iin]) & 0x0f) << 4) | + ((REV64(str[iin + 1]) & 0x3c) >> 2); + iin++; /* 1 used up, iin=2 */ + iout++; - q = buf; - for (p = str; *p; p += 4) { - /* handle escape instructions */ - if (*p == MODE) { - p++; - if (p[0] == MODE && p[1] == MODE) { - /* reset escape chars */ - prot62 = P62; - prot63 = P63; - - p += 2; - } else { - prot62 = *p++; - prot63 = *p++; - } - } - /* since the str is const, we unescape in another buf */ - for (i = 0; i < 4; i++) { - block[i] = p[i]; - if (prot62 == block[i]) { - block[i] = P62; - } else if (prot63 == block[i]) { - block[i] = P63; - } - } - len = decode_token(block, (unsigned char *) q, slen); - q += len; - slen -= 4; - - if (len < 3) + if (iout >= *buflen || iin + 1 >= slen || + str[iin] == '\0' || str[iin + 1] == '\0') break; + ubuf[iout] = ((REV64(str[iin]) & 0x03) << 6) | + (REV64(str[iin + 1]) & 0x3f); + iin += 2; /* 2,3 used up, iin=4 */ + iout++; } - *q = '\0'; - - return q - (unsigned char *) buf; -} + ubuf[iout] = '\0'; + + return iout; +}