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(); }