From: Stephan Bosch Date: Wed, 13 Feb 2019 16:55:31 +0000 (+0100) Subject: lib: base64 - Add support for base64url encoding. X-Git-Tag: 2.3.8~137 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=2d63d58843f6e92d445174276030a31e2f1f197b;p=thirdparty%2Fdovecot%2Fcore.git lib: base64 - Add support for base64url encoding. --- diff --git a/src/lib/base64.c b/src/lib/base64.c index 0b5f004c87..3e7716170a 100644 --- a/src/lib/base64.c +++ b/src/lib/base64.c @@ -186,3 +186,55 @@ struct base64_scheme base64_scheme = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }, }; + +/* + * "base64url" encoding scheme (RFC 4648, Section 5) + */ + +struct base64_scheme base64url_scheme = { + .encmap = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '-', '_', + }, + .decmap = { + 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, 0x3e, 0xff, 0xff, /* 40-47 */ + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, /* 48-55 */ + 0x3c, 0x3d, 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, 0x3f, /* 88-95 */ + 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, /* 96-103 */ + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, /* 104-111 */ + 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, /* 112-119 */ + 0x31, 0x32, 0x33, 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, + }, +}; diff --git a/src/lib/base64.h b/src/lib/base64.h index 664e652309..6c4e5a1c73 100644 --- a/src/lib/base64.h +++ b/src/lib/base64.h @@ -99,4 +99,39 @@ static inline bool base64_is_valid_char(char c) return base64_scheme_is_valid_char(&base64_scheme, c); } +/* + * "base64url" encoding scheme (RFC 4648, Section 5) + */ + +extern struct base64_scheme base64url_scheme; + +/* Translates binary data into base64url. See base64_scheme_encode(). */ +static inline void +base64url_encode(const void *src, size_t src_size, buffer_t *dest) +{ + base64_scheme_encode(&base64url_scheme, src, src_size, dest); +} + +/* Translates base64url data into binary and appends it to dest buffer. See + base64_scheme_decode(). */ +static inline int +base64url_decode(const void *src, size_t src_size, size_t *src_pos_r, + buffer_t *dest) ATTR_NULL(3) +{ + return base64_scheme_decode(&base64url_scheme, src, src_size, + src_pos_r, dest); +} + +/* Decode given string to a buffer allocated from data stack. */ +static inline buffer_t *t_base64url_decode_str(const char *str) +{ + return t_base64_scheme_decode_str(&base64url_scheme, str); +} + +/* Returns TRUE if c is a valid base64url encoding character (excluding '=') */ +static inline bool base64url_is_valid_char(char c) +{ + return base64_scheme_is_valid_char(&base64url_scheme, c); +} + #endif diff --git a/src/lib/test-base64.c b/src/lib/test-base64.c index 3c4abb8926..69cab35a2a 100644 --- a/src/lib/test-base64.c +++ b/src/lib/test-base64.c @@ -4,7 +4,6 @@ #include "str.h" #include "base64.h" - static void test_base64_encode(void) { const struct { @@ -14,6 +13,12 @@ static void test_base64_encode(void) { "hello world", "aGVsbG8gd29ybGQ=" }, { "foo barits", "Zm9vIGJhcml0cw==" }, { "just niin", "anVzdCBuaWlu" }, + { "\xe7\x8c\xbf\xe3\x82\x82\xe6\x9c\xa8\xe3\x81\x8b" + "\xe3\x82\x89\xe8\x90\xbd\xe3\x81\xa1\xe3\x82\x8b", + "54y/44KC5pyo44GL44KJ6JC944Gh44KL" }, + { "\xe8\xa7\x92\xe3\x82\x92\xe7\x9f\xaf\xe3\x82\x81\xe3\x81" + "\xa6\xe7\x89\x9b\xe3\x82\x92\xe6\xae\xba\xe3\x81\x99", + "6KeS44KS55+v44KB44Gm54mb44KS5q6644GZ" }, }; string_t *str; unsigned int i; @@ -53,6 +58,12 @@ static void test_base64_decode(void) "hel", -1, 4 }, { "aGVs!!!!!", "hel", -1, 4 }, + { "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgt" + "C+INC60YPRgCDQtNC+0Y/MgdGCLg==", + "\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc" + "\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0" + "\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc" + "\x81\xd1\x82\x2e", 0, UINT_MAX }, }; string_t *str; buffer_t buf; @@ -118,9 +129,127 @@ static void test_base64_random(void) test_end(); } +static void test_base64url_encode(void) +{ + const struct { + const char *input; + const char *output; + } tests[] = { + { "hello world", "aGVsbG8gd29ybGQ=" }, + { "foo barits", "Zm9vIGJhcml0cw==" }, + { "just niin", "anVzdCBuaWlu" }, + { "\xe7\x8c\xbf\xe3\x82\x82\xe6\x9c\xa8\xe3\x81\x8b" + "\xe3\x82\x89\xe8\x90\xbd\xe3\x81\xa1\xe3\x82\x8b", + "54y_44KC5pyo44GL44KJ6JC944Gh44KL" }, + { "\xe8\xa7\x92\xe3\x82\x92\xe7\x9f\xaf\xe3\x82\x81\xe3\x81" + "\xa6\xe7\x89\x9b\xe3\x82\x92\xe6\xae\xba\xe3\x81\x99", + "6KeS44KS55-v44KB44Gm54mb44KS5q6644GZ" }, + }; + string_t *str; + unsigned int i; + + test_begin("base64url_encode()"); + str = t_str_new(256); + for (i = 0; i < N_ELEMENTS(tests); i++) { + str_truncate(str, 0); + base64url_encode(tests[i].input, strlen(tests[i].input), str); + test_assert_idx(strcmp(tests[i].output, str_c(str)) == 0, i); + test_assert_idx( + str_len(str) == MAX_BASE64_ENCODED_SIZE( + strlen(tests[i].input)), i); + } + test_end(); +} + +struct test_base64url_decode { + const char *input; + const char *output; + int ret; + unsigned int src_pos; +}; + +static void test_base64url_decode(void) +{ + static const struct test_base64url_decode tests[] = { + { "\taGVsbG8gd29ybGQ=", + "hello world", 0, UINT_MAX }, + { "\nZm9v\n \tIGJh \t\ncml0cw==", + "foo barits", 0, UINT_MAX }, + { " anVzdCBuaWlu \n", + "just niin", 1, UINT_MAX }, + { "aGVsb", + "hel", 1, 4 }, + { "aGVsb!!!!!", + "hel", -1, 4 }, + { "aGVs!!!!!", + "hel", -1, 4 }, + { "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgt" + "C-INC60YPRgCDQtNC-0Y_MgdGCLg==", + "\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc" + "\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0" + "\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc" + "\x81\xd1\x82\x2e", 0, UINT_MAX }, + }; + string_t *str; + unsigned int i; + size_t src_pos; + int ret; + + test_begin("base64url_decode()"); + str = t_str_new(256); + for (i = 0; i < N_ELEMENTS(tests); i++) { + str_truncate(str, 0); + + src_pos = 0; + ret = base64url_decode(tests[i].input, strlen(tests[i].input), + &src_pos, str); + + test_assert_idx(tests[i].ret == ret, i); + test_assert_idx(strcmp(tests[i].output, str_c(str)) == 0, i); + test_assert_idx(src_pos == tests[i].src_pos || + (tests[i].src_pos == UINT_MAX && + src_pos == strlen(tests[i].input)), i); + if (ret >= 0) { + test_assert_idx( + str_len(str) <= MAX_BASE64_DECODED_SIZE( + strlen(tests[i].input)), i); + } + } + test_end(); +} + +static void test_base64url_random(void) +{ + string_t *str, *dest; + char buf[10]; + unsigned int i, j, max; + + str = t_str_new(256); + dest = t_str_new(256); + + test_begin("base64url encode/decode with random input"); + for (i = 0; i < 1000; i++) { + max = i_rand_limit(sizeof(buf)); + for (j = 0; j < max; j++) + buf[j] = i_rand(); + + str_truncate(str, 0); + str_truncate(dest, 0); + base64url_encode(buf, max, str); + test_assert_idx(base64url_decode(str_data(str), str_len(str), + NULL, dest) >= 0, i); + test_assert_idx(str_len(dest) == max && + memcmp(buf, str_data(dest), max) == 0, i); + } + test_end(); +} + void test_base64(void) { test_base64_encode(); test_base64_decode(); test_base64_random(); + test_base64url_encode(); + test_base64url_decode(); + test_base64url_random(); }