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