diff options
Diffstat (limited to 'src/base64.c')
-rw-r--r-- | src/base64.c | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/src/base64.c b/src/base64.c new file mode 100644 index 0000000..0601bf6 --- /dev/null +++ b/src/base64.c @@ -0,0 +1,303 @@ +/* + * ASCII <-> Base64 conversion as described in RFC1421. + * + * Copyright 2006-2010 Willy Tarreau <w@1wt.eu> + * Copyright 2009-2010 Krzysztof Piotr Oledzki <ole@ans.pl> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include <stdlib.h> +#include <string.h> + +#include <haproxy/api.h> +#include <haproxy/base64.h> + +#define B64BASE '#' /* arbitrary chosen base value */ +#define B64CMIN '+' +#define UB64CMIN '-' +#define B64CMAX 'z' +#define B64PADV 64 /* Base64 chosen special pad value */ + +const char base64tab[65]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +const char base64rev[]="b###cXYZ[\\]^_`a###d###$%&'()*+,-./0123456789:;<=######>?@ABCDEFGHIJKLMNOPQRSTUVW"; +const char ubase64tab[65]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; +const char ubase64rev[]="b##XYZ[\\]^_`a###c###$%&'()*+,-./0123456789:;<=####c#>?@ABCDEFGHIJKLMNOPQRSTUVW"; + +/* Encodes <ilen> bytes from <in> to <out> for at most <olen> chars (including + * the trailing zero). Returns the number of bytes written. No check is made + * for <in> or <out> to be NULL. Returns negative value if <olen> is too short + * to accept <ilen>. 4 output bytes are produced for 1 to 3 input bytes. + */ +int a2base64(char *in, int ilen, char *out, int olen) +{ + int convlen; + + convlen = ((ilen + 2) / 3) * 4; + + if (convlen >= olen) + return -1; + + /* we don't need to check olen anymore */ + while (ilen >= 3) { + out[0] = base64tab[(((unsigned char)in[0]) >> 2)]; + out[1] = base64tab[(((unsigned char)in[0] & 0x03) << 4) | (((unsigned char)in[1]) >> 4)]; + out[2] = base64tab[(((unsigned char)in[1] & 0x0F) << 2) | (((unsigned char)in[2]) >> 6)]; + out[3] = base64tab[(((unsigned char)in[2] & 0x3F))]; + out += 4; + in += 3; ilen -= 3; + } + + if (!ilen) { + out[0] = '\0'; + } else { + out[0] = base64tab[((unsigned char)in[0]) >> 2]; + if (ilen == 1) { + out[1] = base64tab[((unsigned char)in[0] & 0x03) << 4]; + out[2] = '='; + } else { + out[1] = base64tab[(((unsigned char)in[0] & 0x03) << 4) | + (((unsigned char)in[1]) >> 4)]; + out[2] = base64tab[((unsigned char)in[1] & 0x0F) << 2]; + } + out[3] = '='; + out[4] = '\0'; + } + + return convlen; +} + +/* url variant of a2base64 */ +int a2base64url(const char *in, size_t ilen, char *out, size_t olen) +{ + int convlen; + + convlen = ((ilen + 2) / 3) * 4; + + if (convlen >= olen) + return -1; + + /* we don't need to check olen anymore */ + while (ilen >= 3) { + out[0] = ubase64tab[(((unsigned char)in[0]) >> 2)]; + out[1] = ubase64tab[(((unsigned char)in[0] & 0x03) << 4) | (((unsigned char)in[1]) >> 4)]; + out[2] = ubase64tab[(((unsigned char)in[1] & 0x0F) << 2) | (((unsigned char)in[2]) >> 6)]; + out[3] = ubase64tab[(((unsigned char)in[2] & 0x3F))]; + out += 4; + in += 3; + ilen -= 3; + } + + if (!ilen) { + out[0] = '\0'; + return convlen; + } + + out[0] = ubase64tab[((unsigned char)in[0]) >> 2]; + if (ilen == 1) { + out[1] = ubase64tab[((unsigned char)in[0] & 0x03) << 4]; + out[2] = '\0'; + convlen -= 2; + } else { + out[1] = ubase64tab[(((unsigned char)in[0] & 0x03) << 4) | + (((unsigned char)in[1]) >> 4)]; + out[2] = ubase64tab[((unsigned char)in[1] & 0x0F) << 2]; + out[3] = '\0'; + convlen -= 1; + } + + return convlen; +} + +/* Decodes <ilen> bytes from <in> to <out> for at most <olen> chars. + * Returns the number of bytes converted. No check is made for + * <in> or <out> to be NULL. Returns -1 if <in> is invalid or ilen + * has wrong size, -2 if <olen> is too short. + * 1 to 3 output bytes are produced for 4 input bytes. + */ +int base64dec(const char *in, size_t ilen, char *out, size_t olen) { + + unsigned char t[4]; + signed char b; + int convlen = 0, i = 0, pad = 0; + + if (ilen % 4) + return -1; + + if (olen < ((ilen / 4 * 3) + - (in[ilen-1] == '=' ? 1 : 0) + - (in[ilen-2] == '=' ? 1 : 0))) + return -2; + + while (ilen) { + + /* if (*p < B64CMIN || *p > B64CMAX) */ + b = (signed char)*in - B64CMIN; + if ((unsigned char)b > (B64CMAX-B64CMIN)) + return -1; + + b = base64rev[b] - B64BASE - 1; + + /* b == -1: invalid character */ + if (b < 0) + return -1; + + /* padding has to be continuous */ + if (pad && b != B64PADV) + return -1; + + /* valid padding: "XX==" or "XXX=", but never "X===" or "====" */ + if (pad && i < 2) + return -1; + + if (b == B64PADV) + pad++; + + t[i++] = b; + + if (i == 4) { + /* + * WARNING: we allow to write little more data than we + * should, but the checks from the beginning of the + * functions guarantee that we can safely do that. + */ + + /* xx000000 xx001111 xx111122 xx222222 */ + if (convlen < olen) + out[convlen] = ((t[0] << 2) + (t[1] >> 4)); + if (convlen+1 < olen) + out[convlen+1] = ((t[1] << 4) + (t[2] >> 2)); + if (convlen+2 < olen) + out[convlen+2] = ((t[2] << 6) + (t[3] >> 0)); + + convlen += 3-pad; + + pad = i = 0; + } + + in++; + ilen--; + } + + return convlen; +} + +/* url variant of base64dec */ +/* The reverse tab used to decode base64 is generated via /dev/base64/base64rev-gen.c */ +int base64urldec(const char *in, size_t ilen, char *out, size_t olen) +{ + unsigned char t[4]; + signed char b; + int convlen = 0, i = 0, pad = 0, padlen = 0; + + switch (ilen % 4) { + case 0: + break; + case 2: + padlen = pad = 2; + break; + case 3: + padlen = pad = 1; + break; + default: + return -1; + } + + if (olen < (((ilen + pad) / 4 * 3) - pad)) + return -2; + + while (ilen + pad) { + if (ilen) { + /* if (*p < UB64CMIN || *p > B64CMAX) */ + b = (signed char) * in - UB64CMIN; + if ((unsigned char)b > (B64CMAX - UB64CMIN)) + return -1; + + b = ubase64rev[b] - B64BASE - 1; + /* b == -1: invalid character */ + if (b < 0) + return -1; + + in++; + ilen--; + + } else { + b = B64PADV; + pad--; + } + + t[i++] = b; + + if (i == 4) { + /* + * WARNING: we allow to write little more data than we + * should, but the checks from the beginning of the + * functions guarantee that we can safely do that. + */ + + /* xx000000 xx001111 xx111122 xx222222 */ + if (convlen < olen) + out[convlen] = ((t[0] << 2) + (t[1] >> 4)); + if (convlen+1 < olen) + out[convlen+1] = ((t[1] << 4) + (t[2] >> 2)); + if (convlen+2 < olen) + out[convlen+2] = ((t[2] << 6) + (t[3] >> 0)); + + convlen += 3; + i = 0; + } + } + convlen -= padlen; + + return convlen; +} + +/* Converts the lower 30 bits of an integer to a 5-char base64 string. The + * caller is responsible for ensuring that the output buffer can accept 6 bytes + * (5 + the trailing zero). The pointer to the string is returned. The + * conversion is performed with MSB first and in a format that can be + * decoded with b64tos30(). This format is not padded and thus is not + * compatible with usual base64 routines. + */ +const char *s30tob64(int in, char *out) +{ + int i; + for (i = 0; i < 5; i++) { + out[i] = base64tab[(in >> 24) & 0x3F]; + in <<= 6; + } + out[5] = '\0'; + return out; +} + +/* Converts a 5-char base64 string encoded by s30tob64() into a 30-bit integer. + * The caller is responsible for ensuring that the input contains at least 5 + * chars. If any unexpected character is encountered, a negative value is + * returned. Otherwise the decoded value is returned. + */ +int b64tos30(const char *in) +{ + int i, out; + signed char b; + + out = 0; + for (i = 0; i < 5; i++) { + b = (signed char)in[i] - B64CMIN; + if ((unsigned char)b > (B64CMAX - B64CMIN)) + return -1; /* input character out of range */ + + b = base64rev[b] - B64BASE - 1; + if (b < 0) /* invalid character */ + return -1; + + if (b == B64PADV) /* padding not allowed */ + return -1; + + out = (out << 6) + b; + } + return out; +} |