]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
base64: base64url encoding has no padding
authorPatrick Monnerat <patrick@monnerat.net>
Tue, 12 Jul 2022 17:03:45 +0000 (19:03 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 19 Jul 2022 09:06:20 +0000 (11:06 +0200)
See RFC4648 section 5 and RFC7540 section 3.2.1.

Suppress generation of '=' padding of base64url encoding. This is
accomplished by considering the string beginning at offset 64 in the
character table as the padding: this is "=" for base64, "" for base64url.

Also use strchr() to replace character search loops where possible.

Suppress erroneous comments about empty encoding results.

Adjust unit test 1302 to unpadded base64url encoding and add tests for
empty results.

Closes #9139

lib/base64.c
tests/unit/unit1302.c

index d5d79cf96b4b129227258c0a6dab8ddaca6e72c3..53ebc3db30a44a7ab3754acde1fd2226278cc162 100644 (file)
@@ -43,8 +43,9 @@
 #include "memdebug.h"
 
 /* ---- Base64 Encoding/Decoding Table --- */
+/* Padding character string starts at offset 64. */
 static const char base64[]=
-  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
 
 /* The Base 64 encoding with an URL and filename safe alphabet, RFC 4648
    section 5 */
@@ -54,25 +55,18 @@ static const char base64url[]=
 static size_t decodeQuantum(unsigned char *dest, const char *src)
 {
   size_t padding = 0;
-  const char *s, *p;
+  const char *s;
   unsigned long i, x = 0;
 
   for(i = 0, s = src; i < 4; i++, s++) {
     if(*s == '=') {
-      x = (x << 6);
+      x <<= 6;
       padding++;
     }
     else {
-      unsigned long v = 0;
-      p = base64;
-
-      while(*p && (*p != *s)) {
-        v++;
-        p++;
-      }
-
-      if(*p == *s)
-        x = (x << 6) + v;
+      const char *p = strchr(base64, *s);
+      if(p)
+        x = (x << 6) + curlx_uztoul(p - base64);
       else
         return 0;
     }
@@ -109,11 +103,11 @@ CURLcode Curl_base64_decode(const char *src,
                             unsigned char **outptr, size_t *outlen)
 {
   size_t srclen = 0;
-  size_t length = 0;
   size_t padding = 0;
   size_t i;
   size_t numQuantums;
   size_t rawlen = 0;
+  const char *padptr;
   unsigned char *pos;
   unsigned char *newstr;
 
@@ -126,19 +120,17 @@ CURLcode Curl_base64_decode(const char *src,
     return CURLE_BAD_CONTENT_ENCODING;
 
   /* Find the position of any = padding characters */
-  while((src[length] != '=') && src[length])
-    length++;
-
-  /* A maximum of two = padding characters is allowed */
-  if(src[length] == '=') {
+  padptr = strchr(src, '=');
+  if(padptr) {
     padding++;
-    if(src[length + 1] == '=')
+    /* A maximum of two = padding characters is allowed */
+    if(padptr[1] == '=')
       padding++;
-  }
 
-  /* Check the = padding characters weren't part way through the input */
-  if(length + padding != srclen)
-    return CURLE_BAD_CONTENT_ENCODING;
+    /* Check the = padding characters weren't part way through the input */
+    if(padptr + padding != src + srclen)
+      return CURLE_BAD_CONTENT_ENCODING;
+  }
 
   /* Calculate the number of quantums */
   numQuantums = srclen / 4;
@@ -187,6 +179,7 @@ static CURLcode base64_encode(const char *table64,
   char *output;
   char *base64data;
   const char *indata = inputbuff;
+  const char *padstr = &table64[64];    /* Point to padding string. */
 
   *outptr = NULL;
   *outlen = 0;
@@ -224,27 +217,30 @@ static CURLcode base64_encode(const char *table64,
 
     switch(inputparts) {
     case 1: /* only one byte read */
-      msnprintf(output, 5, "%c%c==",
-                table64[obuf[0]],
-                table64[obuf[1]]);
+      i = msnprintf(output, 5, "%c%c%s%s",
+                    table64[obuf[0]],
+                    table64[obuf[1]],
+                    padstr,
+                    padstr);
       break;
 
     case 2: /* two bytes read */
-      msnprintf(output, 5, "%c%c%c=",
-                table64[obuf[0]],
-                table64[obuf[1]],
-                table64[obuf[2]]);
+      i = msnprintf(output, 5, "%c%c%c%s",
+                    table64[obuf[0]],
+                    table64[obuf[1]],
+                    table64[obuf[2]],
+                    padstr);
       break;
 
     default:
-      msnprintf(output, 5, "%c%c%c%c",
-                table64[obuf[0]],
-                table64[obuf[1]],
-                table64[obuf[2]],
-                table64[obuf[3]]);
+      i = msnprintf(output, 5, "%c%c%c%c",
+                    table64[obuf[0]],
+                    table64[obuf[1]],
+                    table64[obuf[2]],
+                    table64[obuf[3]]);
       break;
     }
-    output += 4;
+    output += i;
   }
 
   /* Zero terminate */
@@ -272,8 +268,6 @@ static CURLcode base64_encode(const char *table64,
  * Returns CURLE_OK on success, otherwise specific error code. Function
  * output shall not be considered valid unless CURLE_OK is returned.
  *
- * When encoded data length is 0, returns NULL in *outptr.
- *
  * @unittest: 1302
  */
 CURLcode Curl_base64_encode(const char *inputbuff, size_t insize,
@@ -295,8 +289,6 @@ CURLcode Curl_base64_encode(const char *inputbuff, size_t insize,
  * Returns CURLE_OK on success, otherwise specific error code. Function
  * output shall not be considered valid unless CURLE_OK is returned.
  *
- * When encoded data length is 0, returns NULL in *outptr.
- *
  * @unittest: 1302
  */
 CURLcode Curl_base64url_encode(const char *inputbuff, size_t insize,
index 77c8b1a74c6a36408ce3a0d36a1b20352d4e749d..12be3c3590a0ad9b7222d590db64c31d68be09cc 100644 (file)
@@ -89,14 +89,14 @@ Curl_safefree(output);
 
 rc = Curl_base64url_encode("\xff\x01\xfe\x02", 4, &output, &size);
 fail_unless(rc == CURLE_OK, "return code should be CURLE_OK");
-fail_unless(size == 8, "size should be 8");
-verify_memory(output, "_wH-Ag==", 8);
+fail_unless(size == 6, "size should be 6");
+verify_memory(output, "_wH-Ag", 6);
 Curl_safefree(output);
 
 rc = Curl_base64url_encode("iiii", 4, &output, &size);
 fail_unless(rc == CURLE_OK, "return code should be CURLE_OK");
-fail_unless(size == 8, "size should be 8");
-verify_memory(output, "aWlpaQ==", 8);
+fail_unless(size == 6, "size should be 6");
+verify_memory(output, "aWlpaQ", 6);
 Curl_safefree(output);
 
 /* 0 length makes it do strlen() */
@@ -106,6 +106,18 @@ fail_unless(size == 8, "size should be 8");
 verify_memory(output, "aWlpaQ==", 8);
 Curl_safefree(output);
 
+rc = Curl_base64_encode("", 0, &output, &size);
+fail_unless(rc == CURLE_OK, "return code should be CURLE_OK");
+fail_unless(size == 0, "size should be 0");
+fail_unless(output && !output[0], "output should be a zero-length string");
+Curl_safefree(output);
+
+rc = Curl_base64url_encode("", 0, &output, &size);
+fail_unless(rc == CURLE_OK, "return code should be CURLE_OK");
+fail_unless(size == 0, "size should be 0");
+fail_unless(output && !output[0], "output should be a zero-length string");
+Curl_safefree(output);
+
 rc = Curl_base64_decode("aWlpaQ==", &decoded, &size);
 fail_unless(rc == CURLE_OK, "return code should be CURLE_OK");
 fail_unless(size == 4, "size should be 4");