RFC 4648 (https://tools.ietf.org/html/rfc4648) standardizes two
Base-64 alphabets. Nettle currently only supports the traditional
base-64 alphabet from section 4.

There is growing use amongst new protocol definitions and extensions,
particularly in the HTTP area for the URL-safe extension alphabet
instead of the classical Base-64 alphabet.

The attached patch implements a proposed API/ABI extension adding
support for RFC 4648 section 5 "Base 64 Encoding with URL and Filename
Safe Alphabet"

For the standardized alphabets external code simply calls the init()
function relevant to the alphabet it is needing to encode/decode with.
The library internally uses the context to select which lookup table to
use for later base64 function calls.

For custom or non-standard alphabets a pointer to the alphabet lookup
table is included in the encode/decode contexts. External code can
memset() a context to empty and provide the alphabet lookup table pointer.

Addtionally this patch adds a simple fuzz unit test for both Base-64 and
Base-64 URL extended alphabet encoders and decoders.


Amos Jeffries
Treehouse Networks Ltd.

PS, I have also removed the dead code wrappend in "#if 0" rather than
updating it.
diff --git a/Makefile.in b/Makefile.in
index 2a940f9..48cce47 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -81,6 +81,7 @@ nettle_SOURCES = aes-decrypt-internal.c aes-decrypt.c \
                 arctwo.c arctwo-meta.c blowfish.c \
                 base16-encode.c base16-decode.c base16-meta.c \
                 base64-encode.c base64-decode.c base64-meta.c \
+                base64url-encode.c base64url-decode.c \
                 buffer.c buffer-init.c \
                 camellia-crypt-internal.c camellia-table.c \
                 camellia-absorb.c camellia-invert-key.c \
diff --git a/base64-decode.c b/base64-decode.c
index f622baa..b603930 100644
--- a/base64-decode.c
+++ b/base64-decode.c
@@ -43,7 +43,7 @@
 #define TABLE_END -3
 
 static const signed char
-decode_table[0x100] =
+default_decode_table[0x100] =
 {
   /* White space is HT, VT, FF, CR, LF and SPC */
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -1, -1, 
@@ -68,6 +68,7 @@ void
 base64_decode_init(struct base64_decode_ctx *ctx)
 {
   ctx->word = ctx->bits = ctx->padding = 0;
+  ctx->alphabet = default_decode_table;
 }
 
 int
@@ -75,9 +76,7 @@ base64_decode_single(struct base64_decode_ctx *ctx,
                     uint8_t *dst,
                     uint8_t src)
 {
-  int data;
-  
-  data = decode_table[src];
+  int data = ctx->alphabet[src];
 
   switch(data)
     {
diff --git a/base64-encode.c b/base64-encode.c
index 313c512..aaa6fa5 100644
--- a/base64-encode.c
+++ b/base64-encode.c
@@ -38,16 +38,24 @@
 
 #include "base64.h"
 
-static const uint8_t encode_table[64] =
+static void _base64_encode_raw(const char *alphabet, uint8_t *dst, size_t 
length, const uint8_t *src);
+
+static const uint8_t default_encode_table[64] =
   "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
   "abcdefghijklmnopqrstuvwxyz"
   "0123456789+/";
 
-#define ENCODE(x) (encode_table[0x3F & (x)])
+#define ENCODE(alphabet,x) ((alphabet)[0x3F & (x)])
 
 void
 base64_encode_raw(uint8_t *dst, size_t length, const uint8_t *src)
 {
+  _base64_encode_raw(default_encode_table, dst, length, src);
+}
+
+void
+_base64_encode_raw(const char *alphabet, uint8_t *dst, size_t length, const 
uint8_t *src)
+{
   const uint8_t *in = src + length;
   uint8_t *out = dst + BASE64_ENCODE_RAW_LENGTH(length);
 
@@ -61,89 +69,46 @@ base64_encode_raw(uint8_t *dst, size_t length, const 
uint8_t *src)
        {
        case 1:
          *--out = '=';
-         *--out = ENCODE(in[0] << 4);
+         *--out = ENCODE(alphabet, (in[0] << 4));
          break;
          
        case 2:
-         *--out = ENCODE( in[1] << 2);
-         *--out = ENCODE((in[0] << 4) | (in[1] >> 4));
+         *--out = ENCODE(alphabet, (in[1] << 2));
+         *--out = ENCODE(alphabet, ((in[0] << 4) | (in[1] >> 4)));
          break;
 
        default:
          abort();
        }
-      *--out = ENCODE(in[0] >> 2);
+      *--out = ENCODE(alphabet, (in[0] >> 2));
     }
   
   while (in > src)
     {
       in -= 3;
-      *--out = ENCODE( in[2]);
-      *--out = ENCODE((in[1] << 2) | (in[2] >> 6));
-      *--out = ENCODE((in[0] << 4) | (in[1] >> 4));
-      *--out = ENCODE( in[0] >> 2);      
+      *--out = ENCODE(alphabet, (in[2]));
+      *--out = ENCODE(alphabet, ((in[1] << 2) | (in[2] >> 6)));
+      *--out = ENCODE(alphabet, ((in[0] << 4) | (in[1] >> 4)));
+      *--out = ENCODE(alphabet, (in[0] >> 2));
     }
   assert(in == src);
   assert(out == dst);
 }
 
-#if 0
-unsigned 
-base64_encode(uint8_t *dst,
-             unsigned src_length,
-             const uint8_t *src)
-{
-  unsigned dst_length = BASE64_ENCODE_RAW_LENGTH(src_length);
-  unsigned n = src_length / 3;
-  unsigned left_over  = src_length % 3;
-  unsigned done = 0;
-  
-  if (left_over)
-    {
-      const uint8_t *in = src + n * 3;
-      uint8_t *out = dst + dst_length;
-
-      switch(left_over)
-       {
-       case 1:
-         *--out = '=';
-         *--out = ENCODE(in[0] << 4);
-         break;
-         
-       case 2:
-         *--out = ENCODE( in[1] << 2);
-         *--out = ENCODE((in[0] << 4) | (in[1] >> 4));
-         break;
-
-       default:
-         abort();
-       }
-      *--out = ENCODE(in[0] >> 2);
-
-      done = 4;
-    }
-  base64_encode_raw(n, dst, src);
-  done += n * 4;
-
-  assert(done == dst_length);
-
-  return done;
-}
-#endif
-
 void
 base64_encode_group(uint8_t *dst, uint32_t group)
 {
-  *dst++ = ENCODE(group >> 18);
-  *dst++ = ENCODE(group >> 12);
-  *dst++ = ENCODE(group >> 6);
-  *dst++ = ENCODE(group);
+  *dst++ = ENCODE(default_encode_table, (group >> 18));
+  *dst++ = ENCODE(default_encode_table, (group >> 12));
+  *dst++ = ENCODE(default_encode_table, (group >> 6));
+  *dst++ = ENCODE(default_encode_table, group);
 }
 
 void
 base64_encode_init(struct base64_encode_ctx *ctx)
 {
   ctx->word = ctx->bits = 0;
+  ctx->alphabet = default_encode_table;
 }
 
 /* Encodes a single byte. */
@@ -159,7 +124,7 @@ base64_encode_single(struct base64_encode_ctx *ctx,
   while (bits >= 6)
     {
       bits -= 6;
-      dst[done++] = ENCODE(word >> bits);
+      dst[done++] = ENCODE(ctx->alphabet, (word >> bits));
     }
 
   ctx->bits = bits;
@@ -196,7 +161,7 @@ base64_encode_update(struct base64_encode_ctx *ctx,
     {
       assert(!(bulk % 3));
       
-      base64_encode_raw(dst + done, bulk, src);
+      _base64_encode_raw(ctx->alphabet, dst + done, bulk, src);
       done += BASE64_ENCODE_RAW_LENGTH(bulk);
       src += bulk;
       left = left_over;
@@ -224,7 +189,7 @@ base64_encode_final(struct base64_encode_ctx *ctx,
   
   if (bits)
     {
-      dst[done++] = ENCODE(ctx->word << (6 - ctx->bits));
+      dst[done++] = ENCODE(ctx->alphabet, (ctx->word << (6 - ctx->bits)));
       for (; bits < 6; bits += 2)
        dst[done++] = '=';
 
diff --git a/base64.h b/base64.h
index a6fb823..21381d2 100644
--- a/base64.h
+++ b/base64.h
@@ -42,12 +42,14 @@ extern "C" {
 
 /* Name mangling */
 #define base64_encode_init nettle_base64_encode_init
+#define base64url_encode_init nettle_base64url_encode_init
 #define base64_encode_single nettle_base64_encode_single
 #define base64_encode_update nettle_base64_encode_update
 #define base64_encode_final nettle_base64_encode_final
 #define base64_encode_raw nettle_base64_encode_raw
 #define base64_encode_group nettle_base64_encode_group
 #define base64_decode_init nettle_base64_decode_init
+#define base64url_decode_init nettle_base64url_decode_init
 #define base64_decode_single nettle_base64_decode_single
 #define base64_decode_update nettle_base64_decode_update
 #define base64_decode_final nettle_base64_decode_final
@@ -73,11 +75,17 @@ struct base64_encode_ctx
 {
   unsigned word;   /* Leftover bits */
   unsigned bits;  /* Number of bits, always 0, 2, or 4. */
+  const uint8_t *alphabet; /* which alphabet to use for encoding */
 };
 
+/* initialize encoding context for base-64 */
 void
 base64_encode_init(struct base64_encode_ctx *ctx);
 
+/* initialize encoding context for base-64 with URL safe extended alphabet */
+void
+base64url_encode_init(struct base64_encode_ctx *ctx);
+
 /* Encodes a single byte. Returns amount of output (always 1 or 2). */
 size_t
 base64_encode_single(struct base64_encode_ctx *ctx,
@@ -123,11 +131,17 @@ struct base64_decode_ctx
 
   /* Number of padding characters encountered */
   unsigned padding;
+  const signed char *alphabet; /* which alphabet to use for encoding */
 };
 
+/* initialize encoding context for base-64 */
 void
 base64_decode_init(struct base64_decode_ctx *ctx);
 
+/* initialize encoding context for base-64 with URL safe extended alphabet */
+void
+base64url_decode_init(struct base64_decode_ctx *ctx);
+
 /* Decodes a single byte. Returns amount of output (0 or 1), or -1 on
  * errors. */
 int
diff --git a/base64url-decode.c b/base64url-decode.c
index e69de29..5b1a802 100644
--- a/base64url-decode.c
+++ b/base64url-decode.c
@@ -0,0 +1,65 @@
+/* base64url-decode.c
+
+   Copyright (C) 2015 Niels Möller
+
+   This file is part of GNU Nettle.
+
+   GNU Nettle is free software: you can redistribute it and/or
+   modify it under the terms of either:
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at your
+       option) any later version.
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at your
+       option) any later version.
+
+   or both in parallel, as here.
+
+   GNU Nettle is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see http://www.gnu.org/licenses/.
+*/
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "base64.h"
+
+static const signed char
+urlextended_decode_table[0x100] =
+{
+  /* White space is HT, VT, FF, CR, LF and SPC */
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+  -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,
+  52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -3, -1, -1,
+  -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+  15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
+  -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+  41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+void
+base64url_decode_init(struct base64_decode_ctx *ctx)
+{
+  ctx->word = ctx->bits = ctx->padding = 0;
+  ctx->alphabet = urlextended_decode_table;
+}
diff --git a/base64url-encode.c b/base64url-encode.c
index e69de29..1f1eab7 100644
--- a/base64url-encode.c
+++ b/base64url-encode.c
@@ -0,0 +1,48 @@
+/* base64url-encode.c
+
+   Copyright (C) 2015 Niels Möller
+
+   This file is part of GNU Nettle.
+
+   GNU Nettle is free software: you can redistribute it and/or
+   modify it under the terms of either:
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at your
+       option) any later version.
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at your
+       option) any later version.
+
+   or both in parallel, as here.
+
+   GNU Nettle is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see http://www.gnu.org/licenses/.
+*/
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "base64.h"
+
+static const uint8_t urlextended_encode_table[64] =
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+  "abcdefghijklmnopqrstuvwxyz"
+  "0123456789-_";
+
+void
+base64url_encode_init(struct base64_encode_ctx *ctx)
+{
+  ctx->word = ctx->bits = 0;
+  ctx->alphabet = urlextended_encode_table;
+}
diff --git a/testsuite/base64-test.c b/testsuite/base64-test.c
index 1633521..8215978 100644
--- a/testsuite/base64-test.c
+++ b/testsuite/base64-test.c
@@ -1,5 +1,56 @@
 #include "testutils.h"
 #include "base64.h"
+#include "knuth-lfib.h"
+
+static void
+test_fuzz_once(struct base64_encode_ctx *ctxEncode, struct base64_decode_ctx 
*ctxDecode, int fuzz_length, uint8_t const * const pattern_in)
+{
+  size_t decoded_len;
+  uint8_t pattern_crypt[2048];
+  uint8_t pattern_out[2048];
+
+  memset(pattern_out, 0, sizeof(pattern_out));
+  memset(pattern_crypt, 0, sizeof(pattern_crypt));
+
+  ASSERT(base64_encode_update(ctxEncode, pattern_crypt, fuzz_length, 
pattern_in) == strlen(pattern_crypt));
+  ASSERT(base64_encode_final(ctxEncode, &pattern_crypt[strlen(pattern_crypt)]) 
<= BASE64_ENCODE_FINAL_LENGTH);
+
+  decoded_len = 0;
+  ASSERT(base64_decode_update(ctxDecode, &decoded_len, pattern_out, 
strlen(pattern_crypt), pattern_crypt) == 1);
+  ASSERT(base64_decode_final(ctxDecode) == 1);
+
+  ASSERT(MEMEQ(1024, pattern_in, pattern_out));
+}
+
+static void
+test_fuzz(void)
+{
+  /* Fuzz a round-trip through both encoder and decoder */
+  struct base64_encode_ctx ctxEncode;
+  struct base64_decode_ctx ctxDecode;
+  int l;
+  size_t fuzz_length;
+  uint8_t pattern_in[1024];
+
+  struct knuth_lfib_ctx ctxRandom;
+  knuth_lfib_init(&ctxRandom, 39854);
+
+  for (l = 0; l < 100000; ++l) {
+    memset(pattern_in, 0, sizeof(pattern_in));
+    fuzz_length = knuth_lfib_get(&ctxRandom) % (sizeof(pattern_in)-4);
+    /* fuzz_length could be 0, which is fine we need to test that case too */
+    knuth_lfib_random(&ctxRandom, fuzz_length, pattern_in);
+
+    base64_encode_init(&ctxEncode);
+    base64_decode_init(&ctxDecode);
+    test_fuzz_once(&ctxEncode, &ctxDecode, fuzz_length, pattern_in);
+
+    base64url_encode_init(&ctxEncode);
+    base64url_decode_init(&ctxDecode);
+    test_fuzz_once(&ctxEncode, &ctxDecode, fuzz_length, pattern_in);
+  }
+
+}
 
 void
 test_main(void)
@@ -45,4 +96,6 @@ test_main(void)
     
     ASSERT(MEMEQ(9, buffer, "HelloG8=x"));
   }
+
+  test_fuzz();
 }
_______________________________________________
nettle-bugs mailing list
[email protected]
http://lists.lysator.liu.se/mailman/listinfo/nettle-bugs

Reply via email to