Mercurial > libbase64
diff base64.c @ 19:436f5c3243bf
Add C implementation
While here, update years
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 24 Jan 2020 13:57:48 +0100 |
parents | |
children | 9205a516264d |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base64.c Fri Jan 24 13:57:48 2020 +0100 @@ -0,0 +1,134 @@ +/* + * base64.h -- base64 encoding and decoding + * + * Copyright (c) 2013-2020 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <assert.h> +#include <ctype.h> +#include <errno.h> + +#include "base64.h" + +static bool +invalid(void) +{ + errno = EILSEQ; + return false; +} + +bool +b64_isbase64(unsigned char ch) +{ + return isalnum(ch) || ch == '+' || ch == '/'; +} + +bool +b64_isvalid(unsigned char ch) +{ + return b64_isbase64(ch) || ch == '='; +} + +unsigned char +b64_lookup(unsigned char value) +{ + assert(value < 64); + + static const char *table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + return table[value]; +} + +unsigned char +b64_rlookup(unsigned char ch) +{ + assert(b64_isbase64(ch)); + + if (ch >= '0' && ch <= '9') + return ch + 4; + if (ch >= 'A' && ch <= 'Z') + return ch - 65; + if (ch >= 'a' && ch <= 'z') + return ch - 71; + + return ch == '+' ? 62U : 63U; +} + +bool +b64_encode_step(const char **pch, char *output) +{ + assert(pch); + assert(output); + + if (!**pch) + return false; + + char inputbuf[3] = { 0 }; + int count = 0; + + output[0] = output[1] = output[2] = output[3] = output[4] = 0; + + while (**pch && count < 3) + inputbuf[count++] = *(*pch)++; + + output[0] = b64_lookup(inputbuf[0] >> 2 & 0x3f); + output[1] = b64_lookup((inputbuf[0] << 4 & 0x3f) | (inputbuf[1] >> 4 & 0x0f)); + output[2] = (count < 2) ? '=' : b64_lookup((inputbuf[1] << 2 & 0x3c) | (inputbuf[2] >> 6 & 0x03)); + output[3] = (count < 3) ? '=' : b64_lookup(inputbuf[2] & 0x3f); + + return count > 0; +} + +bool +b64_decode_step(const char **pch, char *output) +{ + assert(pch); + assert(output); + + if (!**pch) + return false; + + char inputbuf[4] = { -1, -1, -1, -1 }; + int count = 0; + + errno = output[0] = output[1] = output[2] = output[3] = 0; + + for (; **pch && count < 4; count++) { + // Check if the character is valid and get its value. + if ((**pch == '=' && count <= 1) || !b64_isvalid(**pch)) + return invalid(); + if (b64_isbase64(**pch)) + inputbuf[count] = b64_rlookup(**pch); + + (*pch)++; + } + + if (count != 4) + return invalid(); + + output[0] = ((inputbuf[0] << 2) & 0xfc) | ((inputbuf[1] >> 4) & 0x03); + + if (inputbuf[2] != -1) + output[1] = ((inputbuf[1] << 4) & 0xf0) | ((inputbuf[2] >> 2) & 0x0f); + if (inputbuf[3] != -1) { + // "XY=Z" is not allowed. + if (inputbuf[2] == -1) + return invalid(); + + output[2] = ((inputbuf[2] << 6) & 0xc0) | (inputbuf[3] & 0x3f); + } + + return count > 0; +}