X-Git-Url: https://git.ozlabs.org/?p=ccan;a=blobdiff_plain;f=ccan%2Fstr%2Fbase32%2Fbase32.c;fp=ccan%2Fstr%2Fbase32%2Fbase32.c;h=5cdd461bee32a2d4a5841675c3a015dff32f163d;hp=0000000000000000000000000000000000000000;hb=61aee192bcd814b3cb9073bc0280e4ef5cc45c7d;hpb=1e888a93a79132f28f59319558187278badbe418 diff --git a/ccan/str/base32/base32.c b/ccan/str/base32/base32.c new file mode 100644 index 00000000..5cdd461b --- /dev/null +++ b/ccan/str/base32/base32.c @@ -0,0 +1,160 @@ +/* CC0 license (public domain) - see LICENSE file for details */ +#include "base32.h" +#include +#include +#include /* for memcpy, memset */ + +/* RFC 4648: + * + * (1) The final quantum of encoding input is an integral multiple of 40 + * bits; here, the final unit of encoded output will be an integral + * multiple of 8 characters with no "=" padding. + * + * (2) The final quantum of encoding input is exactly 8 bits; here, the + * final unit of encoded output will be two characters followed by + * six "=" padding characters. + * + * (3) The final quantum of encoding input is exactly 16 bits; here, the + * final unit of encoded output will be four characters followed by + * four "=" padding characters. + * + * (4) The final quantum of encoding input is exactly 24 bits; here, the + * final unit of encoded output will be five characters followed by + * three "=" padding characters. + * + * (5) The final quantum of encoding input is exactly 32 bits; here, the + * final unit of encoded output will be seven characters followed by + * one "=" padding character. + */ +static size_t padlen(size_t remainder) +{ + switch (remainder) { + case 0: + return 0; + case 1: + return 6; + case 2: + return 4; + case 3: + return 3; + case 4: + return 1; + default: + abort(); + } +} + +size_t base32_str_size(size_t bytes) +{ + return (bytes + 4) / 5 * 8 + 1; +} + +size_t base32_data_size(const char *str, size_t strlen) +{ + /* 8 chars == 5 bytes, round up to avoid overflow even though + * not required for well-formed strings. */ + size_t max = (strlen + 7) / 8 * 5, padding = 0; + + /* Count trailing padding bytes. */ + while (strlen && str[strlen-1] == '=' && padding < 6) { + strlen--; + padding++; + } + + return max - (padding * 5 + 7) / 8; +} + +static bool decode_8_chars(const char c[8], beint64_t *res, int *bytes) +{ + uint64_t acc = 0; + size_t num_pad = 0; + for (int i = 0; i < 8; i++) { + uint8_t val; + acc <<= 5; + if (c[i] >= 'a' && c[i] <= 'z') + val = c[i] - 'a'; + else if (c[i] >= 'A' && c[i] <= 'Z') + val = c[i] - 'A'; + else if (c[i] >= '2' && c[i] <= '7') + val = c[i] - '2' + 26; + else if (c[i] == '=') { + num_pad++; + continue; + } else + return false; + /* Can't have padding then non-pad */ + if (num_pad) + return false; + acc |= val; + } + *res = cpu_to_be64(acc); + + /* Can't have 2 or 5 padding bytes */ + if (num_pad == 5 || num_pad == 2) + return false; + *bytes = (40 - num_pad * 5) / 8; + return true; +} + +bool base32_decode(const char *str, size_t slen, void *buf, size_t bufsize) +{ + while (slen >= 8) { + beint64_t val; + int bytes; + if (!decode_8_chars(str, &val, &bytes)) + return false; + str += 8; + slen -= 8; + /* Copy bytes into dst. */ + if (bufsize < bytes) + return false; + memcpy(buf, (char *)&val + 3, bytes); + buf = (char *)buf + bytes; + bufsize -= bytes; + } + return slen == 0 && bufsize == 0; +} + +static void encode_8_chars(char *dest, const uint8_t *buf, int bytes) +{ + beint64_t val = 0; + uint64_t res; + int bits = bytes * 8; + static const char enc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + + assert(bytes > 0 && bytes <= 5); + memcpy((char *)&val + 3, buf, bytes); + res = be64_to_cpu(val); + + while (bits > 0) { + *dest = enc[(res >> 35) & 0x1F]; + dest++; + res <<= 5; + bits -= 5; + } + + if (bytes != 5) + memset(dest, '=', padlen(bytes)); +} + +bool base32_encode(const void *buf, size_t bufsize, char *dest, size_t destsize) +{ + while (bufsize) { + int bytes = 5; + + if (bytes > bufsize) + bytes = bufsize; + + if (destsize < 8) + return false; + encode_8_chars(dest, buf, bytes); + buf = (const char *)buf + bytes; + bufsize -= bytes; + destsize -= 8; + dest += 8; + } + if (destsize != 1) + return false; + *dest = '\0'; + return true; +}