diff options
Diffstat (limited to '')
-rw-r--r-- | src/lib/base32.c | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/src/lib/base32.c b/src/lib/base32.c new file mode 100644 index 0000000..89ae978 --- /dev/null +++ b/src/lib/base32.c @@ -0,0 +1,324 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "base32.h" +#include "buffer.h" + +static const char b32enc[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + +static const char b32hexenc[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUV"; + +static const unsigned char b32dec[256] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0-7 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 8-15 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16-23 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 24-31 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32-39 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 40-47 */ + 0xff, 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 48-55 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56-63 */ + 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* 64-71 */ + 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, /* 72-79 */ + 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /* 80-87 */ + 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, /* 88-95 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96-103 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 104-111 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112-119 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 120-127 */ + + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128-255 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const unsigned char b32hexdec[256] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0-7 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 8-15 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16-23 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 24-31 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32-39 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 40-47 */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 48-55 */ + 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56-63 */ + 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, /* 64-71 */ + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, /* 72-79 */ + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0xff, /* 80-87 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 88-95 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96-103 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 104-111 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112-119 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 120-127 */ + + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128-255 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static void +base32_encode_with_alphabet(const char *alph, + bool pad, const void *src, size_t src_size, buffer_t *dest) +{ + const unsigned char *src_c = src; + unsigned char tmp[8], endb; + size_t src_pos; + + /* [5 3][2 5 1][4 4][1 5 2][3 5] + (5)(3 2)(5)(1 4)(4 1)(5)(2 3)(5) + */ + + /* encode main part */ + for (src_pos = 0; src_pos + 4 < src_size; src_pos += 5) { + tmp[0] = alph[src_c[src_pos] >> 3]; + tmp[1] = alph[((src_c[src_pos] & 0x07) << 2) | + ((src_c[src_pos+1] >> 6) & 0x03)]; + tmp[2] = alph[((src_c[src_pos+1] >> 1) & 0x1f)]; + tmp[3] = alph[((src_c[src_pos+1] & 0x01) << 4) | + (src_c[src_pos+2] >> 4)]; + tmp[4] = alph[((src_c[src_pos+2] & 0x0f) << 1) | + (src_c[src_pos+3] >> 7)]; + tmp[5] = alph[((src_c[src_pos+3] >> 2) & 0x1f)]; + tmp[6] = alph[((src_c[src_pos+3] & 0x03) << 3) | + (src_c[src_pos+4] >> 5)]; + tmp[7] = alph[src_c[src_pos+4] & 0x1f]; + buffer_append(dest, tmp, 8); + } + + /* encode last < 5 bytes if any */ + if (src_pos < src_size) { + tmp[0] = alph[src_c[src_pos] >> 3]; + switch (src_size - src_pos) { + case 1: + tmp[1] = alph[((src_c[src_pos] & 0x07) << 2)]; + endb = 2; + break; + case 2: + tmp[1] = alph[((src_c[src_pos] & 0x07) << 2) | + ((src_c[src_pos+1] >> 6) & 0x03)]; + tmp[2] = alph[((src_c[src_pos+1] >> 1) & 0x1f)]; + tmp[3] = alph[((src_c[src_pos+1] & 0x01) << 4)]; + endb = 4; + break; + case 3: + tmp[1] = alph[((src_c[src_pos] & 0x07) << 2) | + ((src_c[src_pos+1] >> 6) & 0x03)]; + tmp[2] = alph[((src_c[src_pos+1] >> 1) & 0x1f)]; + tmp[3] = alph[((src_c[src_pos+1] & 0x01) << 4) | + (src_c[src_pos+2] >> 4)]; + tmp[4] = alph[((src_c[src_pos+2] & 0x0f) << 1)]; + endb = 5; + break; + case 4: + tmp[1] = alph[((src_c[src_pos] & 0x07) << 2) | + ((src_c[src_pos+1] >> 6) & 0x03)]; + tmp[2] = alph[((src_c[src_pos+1] >> 1) & 0x1f)]; + tmp[3] = alph[((src_c[src_pos+1] & 0x01) << 4) | + (src_c[src_pos+2] >> 4)]; + tmp[4] = alph[((src_c[src_pos+2] & 0x0f) << 1) | + (src_c[src_pos+3] >> 7)]; + tmp[5] = alph[((src_c[src_pos+3] >> 2) & 0x1f)]; + tmp[6] = alph[((src_c[src_pos+3] & 0x03) << 3)]; + endb = 7; + break; + default: + i_unreached(); + } + + /* add padding if required */ + if (pad) { + memset(&tmp[endb], '=', sizeof(tmp)-endb); + buffer_append(dest, tmp, 8); + } else { + buffer_append(dest, tmp, endb); + } + } +} + +void base32_encode(bool pad, const void *src, size_t src_size, + buffer_t *dest) +{ + base32_encode_with_alphabet(b32enc, pad, src, src_size, dest); +} + +void base32hex_encode(bool pad, const void *src, size_t src_size, + buffer_t *dest) +{ + base32_encode_with_alphabet(b32hexenc, pad, src, src_size, dest); +} + +#define IS_EMPTY(c) \ + ((c) == '\n' || (c) == '\r' || (c) == ' ' || (c) == '\t') + +static int +base32_decode_with_alphabet(const unsigned char *alph, + const void *src, size_t src_size, size_t *src_pos_r, + buffer_t *dest) +{ + const unsigned char *src_c = src; + size_t block_pos, src_pos; + unsigned char output[5], ipos, opos; + int ret = 1; + + /* (5)(3 2)(5)(1 4)(4 1)(5)(2 3)(5) + [5 3][2 5 1][4 4][1 5 2][3 5] + */ + ipos = opos = 0; + block_pos = 0; + for (src_pos = 0; src_pos < src_size; src_pos++) { + unsigned char input = alph[src_c[src_pos]]; + + if (input == 0xff) { + if (unlikely(!IS_EMPTY(src_c[src_pos]))) + break; + continue; + } + + ipos++; + switch (ipos) { + case 1: + output[0] = input << 3; + opos = 0; + break; + case 2: + output[0] |= input >> 2; + output[1] = (input & 0x03) << 6; + opos = 1; + break; + case 3: + output[1] |= input << 1; + opos = 1; + break; + case 4: + output[1] |= input >> 4; + output[2] = (input & 0x0f) << 4; + opos = 2; + break; + case 5: + output[2] |= input >> 1; + output[3] = (input & 0x01) << 7; + opos = 3; + break; + case 6: + output[3] |= input << 2; + opos = 3; + break; + case 7: + output[3] |= input >> 3; + output[4] = ((input & 0x07) << 5); + opos = 4; + break; + case 8: + output[4] |= input; + buffer_append(dest, output, 5); + ipos = 0; + opos = 0; + block_pos = src_pos; + break; + default: + i_unreached(); + } + } + + if (ipos > 0) { + for (; src_pos < src_size; src_pos++) { + if (src_c[src_pos] != '=') { + if (unlikely(!IS_EMPTY(src_c[src_pos]))) { + ret = -1; + break; + } + continue; + } + if (++ipos >= 8) { + buffer_append(dest, output, opos); + ipos = 0; + ret = 0; + src_pos++; + break; + } + } + } + + if (src_pos_r != NULL) { + if (ipos == 0) { + for (; src_pos < src_size; src_pos++) { + if (!IS_EMPTY(src_c[src_pos])) + break; + } + + *src_pos_r = src_pos; + } else { + *src_pos_r = block_pos; + } + } + return ret; +} + +int base32_decode(const void *src, size_t src_size, + size_t *src_pos_r, buffer_t *dest) +{ + return base32_decode_with_alphabet + (b32dec, src, src_size, src_pos_r, dest); +} +int base32hex_decode(const void *src, size_t src_size, + size_t *src_pos_r, buffer_t *dest) +{ + return base32_decode_with_alphabet + (b32hexdec, src, src_size, src_pos_r, dest); +} + +buffer_t *t_base32_decode_str(const char *str) +{ + buffer_t *buf; + size_t len = strlen(str); + + buf = t_buffer_create(MAX_BASE32_DECODED_SIZE(len)); + (void)base32_decode(str, len, NULL, buf); + return buf; +} + +buffer_t *t_base32hex_decode_str(const char *str) +{ + buffer_t *buf; + size_t len = strlen(str); + + buf = t_buffer_create(MAX_BASE32_DECODED_SIZE(len)); + (void)base32hex_decode(str, len, NULL, buf); + return buf; +} + +bool base32_is_valid_char(char c) +{ + return b32dec[(uint8_t)c] != 0xff; +} + +bool base32hex_is_valid_char(char c) +{ + return b32hexdec[(uint8_t)c] != 0xff; +} |