Mercurial > libbase64
changeset 31:16f64f665b1d
base64: fix invalid compute on binary data
While here, add support for base64url decoding.
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 02 Jun 2021 15:48:33 +0200 |
parents | 74316437d97e |
children | c96f2b26678a |
files | Makefile base64.c test/base64.c |
diffstat | 3 files changed, 79 insertions(+), 50 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Mon May 17 14:02:41 2021 +0200 +++ b/Makefile Wed Jun 02 15:48:33 2021 +0200 @@ -20,8 +20,8 @@ CC= cc CXX= c++ -CFLAGS= -O3 -DNDEBUG -CXXFLAGS= -std=c++17 -O3 -DNDEBUG +CFLAGS= -O3 -DNDEBUG -Wall -Wextra +CXXFLAGS= -std=c++17 -O3 -DNDEBUG -Wall -Wextra .SUFFIXES: .SUFFIXES: .c .cpp .o
--- a/base64.c Mon May 17 14:02:41 2021 +0200 +++ b/base64.c Wed Jun 02 15:48:33 2021 +0200 @@ -26,7 +26,7 @@ int b64_isbase64(unsigned char ch) { - return isalnum(ch) || ch == '+' || ch == '/'; + return isalnum(ch) || ch == '+' || ch == '-' || ch == '_' || ch == '/'; } int @@ -57,7 +57,8 @@ if (ch >= 'a' && ch <= 'z') return ch - 71; - return ch == '+' ? 62U : 63U; + /* '-' is base64url support. */ + return ch == '+' || ch == '-' ? 62U : 63U; } size_t @@ -139,49 +140,46 @@ srcsz = strlen(src); while (srcsz && dstsz) { - unsigned int parsed = 0, required, inputbuf[4] = {0}; + int i = 0, r = 3; + unsigned int inputbuf[4] = {0}; - for (; srcsz && parsed < 4; parsed++) { - /* '=' is only allowed in last 2 characters. */ - if ((*src == '=' && parsed <= 1) || !b64_isvalid(*src)) + for (; srcsz && i < 4; i++) { + if (*src == '=') { + /* + * '=' is only allowed in last 2 characters, + * otherwise it means we need less data. + */ + if (i <= 1) + goto eilseq; + + /* Less data required. */ + --r; + } else if (!b64_isvalid(*src)) goto eilseq; + if (b64_isbase64(*src)) - inputbuf[parsed] = b64_rlookup(*src); + inputbuf[i] = b64_rlookup(*src); ++src; --srcsz; } - if (parsed != 4) + /* Make sure we haven't seen AB=Z as well. */ + if (i != 4 || (src[-2] == '=' && src[-1] != '=')) goto eilseq; - - if (inputbuf[3]) - required = 3; - else if (inputbuf[2]) - required = 2; - else - required = 1; - - if (required >= dstsz) + if ((size_t)r >= dstsz) goto erange; *dst++ = ((inputbuf[0] << 2) & 0xfc) | ((inputbuf[1] >> 4) & 0x03); - if (inputbuf[2]) - *dst++ = ((inputbuf[1] << 4) & 0xf0) | - ((inputbuf[2] >> 2) & 0x0f); - if (inputbuf[3]) { - /* "XY=Z" is not allowed. */ - if (inputbuf[2] == 0) - goto eilseq; + if (r >= 2) + *dst++ = ((inputbuf[1] << 4) & 0xf0) | ((inputbuf[2] >> 2) & 0x0f); + if (r >= 3) + *dst++ = ((inputbuf[2] << 6) & 0xc0) | (inputbuf[3] & 0x3f); - *dst++ = ((inputbuf[2] << 6) & 0xc0) | - (inputbuf[3] & 0x3f); - } - - nwritten += required; - dstsz -= required; + nwritten += r; + dstsz -= r; } /* Not enough room to store '\0'. */
--- a/test/base64.c Mon May 17 14:02:41 2021 +0200 +++ b/test/base64.c Wed Jun 02 15:48:33 2021 +0200 @@ -262,27 +262,57 @@ GREATEST_TEST test_b64_decode_basic(void) { + static const struct { + const char *entry; + const char *exp; + size_t expsz; + } examples[] = { + { "YQ==", "a", 1U }, + { "YWI=", "ab", 2U }, + { "YWJj", "abc", 3U }, + { "aGVsbG8=", "hello", 5U }, + { "dGhpcyBpcyBhIGxvbmcgc2VudGVuY2U=", "this is a long sentence", 23U }, + { "V2VsY29tZSB0byBvdXIgc2VydmVyIGR1ZGU=", "Welcome to our server dude", 26U }, + { NULL, NULL, 0 } + }; + char buffer[BUFSIZ]; size_t r; - r = b64_decode("YQ==", -1, buffer, sizeof (buffer)); - GREATEST_ASSERT_EQ(r, 1U); - GREATEST_ASSERT_STR_EQ(buffer, "a"); - r = b64_decode("YWI=", -1, buffer, sizeof (buffer)); - GREATEST_ASSERT_EQ(r, 2U); - GREATEST_ASSERT_STR_EQ(buffer, "ab"); - r = b64_decode("YWJj", -1, buffer, sizeof (buffer)); - GREATEST_ASSERT_EQ(r, 3U); - GREATEST_ASSERT_STR_EQ(buffer, "abc"); - r = b64_decode("aGVsbG8=", -1, buffer, sizeof (buffer)); - GREATEST_ASSERT_EQ(r, 5U); - GREATEST_ASSERT_STR_EQ(buffer, "hello"); - r = b64_decode("dGhpcyBpcyBhIGxvbmcgc2VudGVuY2U=", -1, buffer, sizeof (buffer)); - GREATEST_ASSERT_EQ(r, 23U); - GREATEST_ASSERT_STR_EQ(buffer, "this is a long sentence"); - r = b64_decode("V2VsY29tZSB0byBvdXIgc2VydmVyIGR1ZGU=", -1, buffer, sizeof (buffer)); - GREATEST_ASSERT_EQ(r, 26U); - GREATEST_ASSERT_STR_EQ(buffer, "Welcome to our server dude"); + for (size_t i = 0; examples[i].entry; ++i) { + r = b64_decode(examples[i].entry, -1, buffer, sizeof (buffer)); + GREATEST_ASSERT_EQ(r, examples[i].expsz); + GREATEST_ASSERT_STR_EQ(buffer, examples[i].exp); + } + + GREATEST_PASS(); +} + +GREATEST_TEST +test_b64_decode_binary(void) +{ + static const struct { + const char *entry; + size_t expsz; + } examples[] = { + { "4oARcAAAAgoogyHe", 12 }, + { "ERADdw==", 4 }, + { "4oARkaUCAGAbAtfN", 12 }, + { NULL, 0 } + }; + + char bin[BUFSIZ], b64[BUFSIZ]; + size_t r; + + for (size_t i = 0; examples[i].entry; ++i) { + /* Compare by decoding and coding back again. */ + r = b64_decode(examples[i].entry, -1, bin, sizeof (bin)); + GREATEST_ASSERT_EQ(r, examples[i].expsz); + r = b64_encode(bin, r, b64, sizeof (b64)); + GREATEST_ASSERT_EQ(r, strlen(examples[i].entry)); + GREATEST_ASSERT_STR_EQ(b64, examples[i].entry); + } + GREATEST_PASS(); } @@ -407,6 +437,7 @@ GREATEST_SUITE(suite_b64_decode) { GREATEST_RUN_TEST(test_b64_decode_basic); + GREATEST_RUN_TEST(test_b64_decode_binary); GREATEST_RUN_TEST(test_b64_decode_sized); GREATEST_RUN_TEST(test_b64_decode_invalid); GREATEST_RUN_TEST(test_b64_decode_toosmall);