#include "base64.h" static int mg_base64_encode_single(int c) { if (c < 26) { return c + 'A'; } else if (c < 52) { return c - 26 + 'a'; } else if (c < 62) { return c - 52 + '0'; } else { return c == 62 ? '+' : '/'; } } static int base64_decode_single(int c, char plus, char slash) { if (c >= 'A' && c <= 'Z') { return c - 'A'; } else if (c >= 'a' && c <= 'z') { return c + 26 - 'a'; } else if (c >= '0' && c <= '9') { return c + 52 - '0'; } else if (c == plus) { return 62; } else if (c == slash) { return 63; } else if (c == '=') { return 64; } else { return -1; } } static int mg_base64_decode_single(int c) { return base64_decode_single(c, '+', '/'); } static int mg_base64url_decode_single(int c) { return base64_decode_single(c, '-', '_'); } size_t mg_base64_update(unsigned char ch, char *to, size_t n) { unsigned long rem = (n & 3) % 3; if (rem == 0) { to[n] = (char) mg_base64_encode_single(ch >> 2); to[++n] = (char) ((ch & 3) << 4); } else if (rem == 1) { to[n] = (char) mg_base64_encode_single(to[n] | (ch >> 4)); to[++n] = (char) ((ch & 15) << 2); } else { to[n] = (char) mg_base64_encode_single(to[n] | (ch >> 6)); to[++n] = (char) mg_base64_encode_single(ch & 63); n++; } return n; } size_t mg_base64_final(char *to, size_t n) { size_t saved = n; // printf("---[%.*s]\n", n, to); if (n & 3) n = mg_base64_update(0, to, n); if ((saved & 3) == 2) n--; // printf(" %d[%.*s]\n", n, n, to); while (n & 3) to[n++] = '='; to[n] = '\0'; return n; } size_t mg_base64_encode(const unsigned char *p, size_t n, char *to, size_t dl) { size_t i, len = 0; if (dl > 0) to[0] = '\0'; if (dl < ((n / 3) + (n % 3 ? 1 : 0)) * 4 + 1) return 0; for (i = 0; i < n; i++) len = mg_base64_update(p[i], to, len); len = mg_base64_final(to, len); return len; } size_t mg_base64_decode(const char *src, size_t n, char *dst, size_t dl) { const char *end = src == NULL ? NULL : src + n; // Cannot add to NULL size_t len = 0; if (dl < n / 4 * 3 + 1) goto fail; while (src != NULL && src + 3 < end) { int a = mg_base64_decode_single(src[0]), b = mg_base64_decode_single(src[1]), c = mg_base64_decode_single(src[2]), d = mg_base64_decode_single(src[3]); if (a == 64 || a < 0 || b == 64 || b < 0 || c < 0 || d < 0) { goto fail; } dst[len++] = (char) ((a << 2) | (b >> 4)); if (src[2] != '=') { dst[len++] = (char) ((b << 4) | (c >> 2)); if (src[3] != '=') dst[len++] = (char) ((c << 6) | d); } src += 4; } dst[len] = '\0'; return len; fail: if (dl > 0) dst[0] = '\0'; return 0; } size_t mg_base64url_encode(const unsigned char *p, size_t n, char *to, size_t dl) { size_t i, len = mg_base64_encode(p, n, to, dl); if (len == 0) return 0; for (i = 0; i < len; i++) { if (to[i] == '+') { to[i] = '-'; } else if (to[i] == '/') { to[i] = '_'; } } while (len > 0 && to[len - 1] == '=') to[--len] = '\0'; return len; } size_t mg_base64url_decode(const char *src, size_t n, char *dst, size_t dl) { size_t i, len = 0; unsigned int bits = 0, v = 0; if (dl == 0 || (n & 3) == 1) goto fail; for (i = 0; src != NULL && i < n; i++) { int c = mg_base64url_decode_single(src[i]); if (c == 64) break; if (c < 0) goto fail; v = (v << 6) | (unsigned int) c; bits += 6; if (bits >= 8) { bits -= 8; if (len + 1 >= dl) goto fail; dst[len++] = (char) ((v >> bits) & 255U); } } dst[len] = '\0'; return len; fail: if (dl > 0) dst[0] = '\0'; return 0; }