]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: base64 - Add support for decoding without padding.
authorStephan Bosch <stephan.bosch@dovecot.fi>
Fri, 17 May 2019 08:17:19 +0000 (10:17 +0200)
committerVille Savolainen <ville.savolainen@dovecot.fi>
Tue, 10 Sep 2019 07:02:28 +0000 (10:02 +0300)
src/lib/base64.c
src/lib/base64.h
src/lib/test-base64.c

index fb8eeace98c7fd5dfd640e47d542bdadc8fc658e..2994e59ee18b8b8b8a805bf0329ce47e1ab6b4a1 100644 (file)
@@ -588,58 +588,65 @@ int base64_decode_more(struct base64_decoder *dec,
                /* try to parse the end (padding) of the base64 input */
                i_assert(src_pos < src_size);
 
-               switch (dec->sub_pos) {
-               case 0:
-               case 1:
-                       /* no padding expected */
+               if (HAS_ALL_BITS(dec->flags,
+                                BASE64_DECODE_FLAG_NO_PADDING)) {
+                       /* no padding allowed */
+                       i_assert(!dec->seen_padding);
                        ret = -1;
-                       break;
-               case 2:
-                       if (unlikely(src_c[src_pos] != '=')) {
-                               /* invalid character */
-                               ret = -1;
-                               break;
-                       }
-                       dec->seen_padding = TRUE;
-                       dec->sub_pos++;
-                       src_pos++;
-                       if (src_pos == src_size) {
-                               ret = 1;
-                               break;
-                       }
-                       /* skip any whitespace in the padding */
-                       base64_skip_whitespace(dec, src_c, src_size,
-                                              &src_pos);
-                       if (src_pos == src_size) {
-                               ret = 1;
-                               break;
-                       }
-                       /* fall through */
-               case 3:
-                       if (unlikely(src_c[src_pos] != '=')) {
-                               /* invalid character */
+               } else {
+                       switch (dec->sub_pos) {
+                       case 0:
+                       case 1:
+                               /* no padding expected */
                                ret = -1;
                                break;
-                       }
-                       dec->seen_padding = TRUE;
-                       dec->seen_end = TRUE;
-                       dec->sub_pos = 0;
-                       src_pos++;
-                       /* skip any trailing whitespace */
-                       base64_skip_whitespace(dec, src_c, src_size,
-                                              &src_pos);
-                       if (src_pos < src_size) {
-                               ret = -1;
+                       case 2:
+                               if (unlikely(src_c[src_pos] != '=')) {
+                                       /* invalid character */
+                                       ret = -1;
+                                       break;
+                               }
+                               dec->seen_padding = TRUE;
+                               dec->sub_pos++;
+                               src_pos++;
+                               if (src_pos == src_size) {
+                                       ret = 1;
+                                       break;
+                               }
+                               /* skip any whitespace in the padding */
+                               base64_skip_whitespace(dec, src_c, src_size,
+                                                      &src_pos);
+                               if (src_pos == src_size) {
+                                       ret = 1;
+                                       break;
+                               }
+                               /* fall through */
+                       case 3:
+                               if (unlikely(src_c[src_pos] != '=')) {
+                                       /* invalid character */
+                                       ret = -1;
+                                       break;
+                               }
+                               dec->seen_padding = TRUE;
+                               dec->seen_end = TRUE;
+                               dec->sub_pos = 0;
+                               src_pos++;
+                               /* skip any trailing whitespace */
+                               base64_skip_whitespace(dec, src_c, src_size,
+                                                      &src_pos);
+                               if (src_pos < src_size) {
+                                       ret = -1;
+                                       break;
+                               }
+                               if (no_whitespace) {
+                                       dec->seen_boundary = TRUE;
+                                       ret = 0;
+                               } else {
+                                       /* more whitespace may follow */
+                                       ret = 1;
+                               }
                                break;
                        }
-                       if (no_whitespace) {
-                               dec->seen_boundary = TRUE;
-                               ret = 0;
-                       } else {
-                               /* more whitespace may follow */
-                               ret = 1;
-                       }
-                       break;
                }
        }
 
@@ -661,7 +668,13 @@ int base64_decode_finish(struct base64_decoder *dec)
        i_assert(!dec->finished);
        dec->finished = TRUE;
 
-       return (!dec->failed && dec->sub_pos == 0 ? 0 : -1);
+       if (dec->failed)
+               return -1;
+
+       if (HAS_ALL_BITS(dec->flags,
+                        BASE64_DECODE_FLAG_NO_PADDING))
+               return 0;
+       return (dec->sub_pos == 0 ? 0 : -1);
 }
 
 /*
index 751adf10bf654e4d2251b389120274de5fdffefe..28215529eb58544a849a7566226fc20d17c35a2e 100644 (file)
@@ -116,6 +116,8 @@ enum base64_decode_flags {
        BASE64_DECODE_FLAG_EXPECT_BOUNDARY = BIT(0),
        /* Prohibit whitespace in the input. */
        BASE64_DECODE_FLAG_NO_WHITESPACE   = BIT(1),
+       /* Require absence of padding at the end of the input. */
+       BASE64_DECODE_FLAG_NO_PADDING      = BIT(2),
 };
 
 struct base64_decoder {
index 6e944694f39dc12eefb3443b3629a4286e13c93f..e9e08dcb41763c77b217d0459cc18f6be0630c41 100644 (file)
@@ -518,6 +518,22 @@ tests_base64_decode_lowlevel[] = {
                .src_pos = 0,
                .flags = BASE64_DECODE_FLAG_NO_WHITESPACE,
        },
+       {
+               .scheme = &base64_scheme,
+               .flags = BASE64_DECODE_FLAG_NO_PADDING,
+               .input = "\taGVsbG8gd29ybGQ=",
+               .output = "hello world",
+               .ret = -1,
+               .src_pos = 16,
+       },
+       {
+               .scheme = &base64_scheme,
+               .flags = BASE64_DECODE_FLAG_NO_PADDING,
+               .input = "\taGVsbG8gd29ybGQ",
+               .output = "hello world",
+               .ret = 0,
+               .src_pos = 16,
+       },
        {
                .scheme = &base64_scheme,
                .input = "\nZm9v\n \tIGJh  \t\ncml0cw==",
@@ -553,6 +569,22 @@ tests_base64_decode_lowlevel[] = {
                .ret = 0,
                .src_pos = UINT_MAX,
        },
+       {
+               .scheme = &base64_scheme,
+               .flags = BASE64_DECODE_FLAG_NO_PADDING,
+               .input = "\nZm9v\n \tIGJh  \t\ncml0cw==",
+               .output = "foo barits",
+               .ret = -1,
+               .src_pos = 22,
+       },
+       {
+               .scheme = &base64_scheme,
+               .flags = BASE64_DECODE_FLAG_NO_PADDING,
+               .input = "\nZm9v\n \tIGJh  \t\ncml0cw",
+               .output = "foo barits",
+               .ret = 0,
+               .src_pos = 22,
+       },
        {
                .scheme = &base64_scheme,
                .input = "  anVzdCBuaWlu  \n",
@@ -567,6 +599,14 @@ tests_base64_decode_lowlevel[] = {
                .ret = 0,
                .src_pos = UINT_MAX,
        },
+       {
+               .scheme = &base64_scheme,
+               .flags = BASE64_DECODE_FLAG_NO_PADDING,
+               .input = "  anVzdCBuaWlu  \n",
+               .output = "just niin",
+               .ret = 0,
+               .src_pos = UINT_MAX,
+       },
        {
                .scheme = &base64_scheme,
                .input = "aGVsb",
@@ -581,6 +621,14 @@ tests_base64_decode_lowlevel[] = {
                .ret = -1,
                .src_pos = 5,
        },
+       {
+               .scheme = &base64_scheme,
+               .flags = BASE64_DECODE_FLAG_NO_PADDING,
+               .input = "aGVsb",
+               .output = "hel",
+               .ret = 0,
+               .src_pos = 5,
+       },
        {
                .scheme = &base64_scheme,
                .input = "aGVsb!!!!!",
@@ -635,6 +683,20 @@ tests_base64_decode_lowlevel[] = {
                .ret = 0,
                .src_pos = UINT_MAX,
        },
+       {
+               .scheme = &base64_scheme,
+               .flags = BASE64_DECODE_FLAG_NO_PADDING,
+               .input =
+                       "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgt"
+                       "C+INC60YPRgCDQtNC+0Y/MgdGCLg",
+               .output =
+                       "\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",
+               .ret = 0,
+               .src_pos = UINT_MAX,
+       },
 };
 
 static void test_base64_decode_lowlevel(void)
@@ -889,6 +951,20 @@ test_base64_random_lowlevel(void)
                                         BASE64_ENCODE_FLAG_CRLF, 0, 10);
        test_base64_random_lowlevel_case(&base64url_scheme,
                                         BASE64_ENCODE_FLAG_CRLF, 0, 10);
+       test_base64_random_lowlevel_case(&base64_scheme,
+                                        BASE64_ENCODE_FLAG_NO_PADDING,
+                                        BASE64_DECODE_FLAG_NO_PADDING, 0);
+       test_base64_random_lowlevel_case(&base64url_scheme,
+                                        BASE64_ENCODE_FLAG_NO_PADDING,
+                                        BASE64_DECODE_FLAG_NO_PADDING, 0);
+       test_base64_random_lowlevel_case(&base64_scheme,
+                                        BASE64_ENCODE_FLAG_NO_PADDING |
+                                        BASE64_ENCODE_FLAG_CRLF,
+                                        BASE64_DECODE_FLAG_NO_PADDING, 15);
+       test_base64_random_lowlevel_case(&base64url_scheme,
+                                        BASE64_ENCODE_FLAG_NO_PADDING |
+                                        BASE64_ENCODE_FLAG_CRLF,
+                                        BASE64_DECODE_FLAG_NO_PADDING, 15);
        test_end();
 }