summaryrefslogtreecommitdiffstats
path: root/src/lib/test-istream-base64-decoder.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lib/test-istream-base64-decoder.c337
1 files changed, 337 insertions, 0 deletions
diff --git a/src/lib/test-istream-base64-decoder.c b/src/lib/test-istream-base64-decoder.c
new file mode 100644
index 0000000..ea45f6d
--- /dev/null
+++ b/src/lib/test-istream-base64-decoder.c
@@ -0,0 +1,337 @@
+/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
+
+#include "test-lib.h"
+#include "str.h"
+#include "istream-private.h"
+#include "istream-base64.h"
+#include "istream-sized.h"
+#include "base64.h"
+
+struct base64_istream_test {
+ const char *input;
+ const char *output;
+ int stream_errno;
+};
+
+static const struct base64_istream_test base64_tests[] = {
+ { "", "", 0 },
+ { "aGVsbG8gd29ybGQ=", "hello world", 0 },
+ { "\naGVs\nbG8g\nd29y\nbGQ=\n", "hello world", 0 },
+ { " aGVs \r\n bG8g \r\n d29y \t \r\n bGQ= \r\n\r\n",
+ "hello world", 0 },
+ { "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgtC+INC60YPRgCDQtNC+0Y/MgdGCLg==",
+ "\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", 0 },
+ { "\r", "", 0 },
+ { "\n", "", 0 },
+ { "\r\n", "", 0 },
+ { " ", "", 0 },
+ { "foo", "\x7e\x8a", EPIPE },
+ { "foo ","\x7e\x8a", EPIPE },
+ { "Zm9vC", "foo", EPIPE },
+ { "Zm9v!", "foo", EINVAL },
+ { "Zm9!v", "fo", EINVAL },
+ { "Zm9 v", "foo", 0 },
+ { "Zm 9v", "foo", 0 },
+ { "Z m9v", "foo", 0 },
+};
+
+static const struct base64_istream_test base64url_tests[] = {
+ { "", "", 0 },
+ { "aGVsbG8gd29ybGQ=", "hello world", 0 },
+ { "\naGVs\nbG8g\nd29y\nbGQ=\n", "hello world", 0 },
+ { " aGVs \r\n bG8g \r\n d29y \t \r\n bGQ= \r\n\r\n",
+ "hello world", 0 },
+ { "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgtC-INC60YPRgCDQtNC-0Y_MgdGCLg==",
+ "\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", 0 },
+ { "\r", "", 0 },
+ { "\n", "", 0 },
+ { "\r\n", "", 0 },
+ { " ", "", 0 },
+ { "foo", "\x7e\x8a", EPIPE },
+ { "foo ","\x7e\x8a", EPIPE },
+ { "Zm9vC", "foo", EPIPE },
+ { "Zm9v!", "foo", EINVAL },
+ { "Zm9!v", "fo", EINVAL },
+ { "Zm9 v", "foo", 0 },
+ { "Zm 9v", "foo", 0 },
+ { "Z m9v", "foo", 0 },
+};
+
+static void
+decode_test(unsigned int base64_input_len,
+ struct istream *input_data, struct istream *input,
+ const char *output, int stream_errno)
+{
+ const unsigned char *data;
+ size_t i, size;
+ int ret = 0;
+
+ for (i = 1; i <= base64_input_len; i++) {
+ test_istream_set_size(input_data, i);
+ while ((ret = i_stream_read(input)) > 0) ;
+ if (ret == -1 && stream_errno != 0)
+ break;
+ test_assert(ret == 0);
+ }
+ if (ret == 0) {
+ test_istream_set_allow_eof(input_data, TRUE);
+ while ((ret = i_stream_read(input)) > 0) ;
+ }
+ test_assert(ret == -1);
+ test_assert(input->stream_errno == stream_errno);
+
+ data = i_stream_get_data(input, &size);
+ test_assert(size == strlen(output));
+ if (size > 0)
+ test_assert(memcmp(data, output, size) == 0);
+}
+
+static void
+decode_base64_test(const char *base64_input, const char *output,
+ int stream_errno)
+{
+ unsigned int base64_input_len = strlen(base64_input);
+ struct istream *input_data, *input;
+
+ input_data = test_istream_create_data(base64_input, base64_input_len);
+ test_istream_set_allow_eof(input_data, FALSE);
+ input = i_stream_create_base64_decoder(input_data);
+
+ decode_test(base64_input_len, input_data, input, output, stream_errno);
+
+ i_stream_unref(&input);
+ i_stream_unref(&input_data);
+}
+
+static void
+decode_base64url_test(const char *base64_input, const char *output,
+ int stream_errno)
+{
+ unsigned int base64_input_len = strlen(base64_input);
+ struct istream *input_data, *input;
+
+ input_data = test_istream_create_data(base64_input, base64_input_len);
+ test_istream_set_allow_eof(input_data, FALSE);
+ input = i_stream_create_base64url_decoder(input_data);
+
+ decode_test(base64_input_len, input_data, input, output, stream_errno);
+
+ i_stream_unref(&input);
+ i_stream_unref(&input_data);
+}
+
+static void
+test_istream_base64_io_random(void)
+{
+ unsigned char in_buf[2048];
+ size_t in_buf_size;
+ buffer_t *out_buf;
+ unsigned int i, j;
+ int ret;
+
+ out_buf = t_buffer_create(sizeof(in_buf));
+
+ test_begin("istream base64 random I/O");
+
+ for (i = 0; !test_has_failed() && i < 4000; i++) {
+ struct istream *input1, *input2, *input3, *input4, *input5;
+ struct istream *sinput1, *sinput2, *sinput3, *sinput4;
+ struct istream *top_input;
+ const unsigned char *data;
+ unsigned int chpl1, chpl2;
+ unsigned int sized_streams;
+ unsigned int crlf_encode;
+ size_t size;
+ struct base64_encoder b64enc;
+
+ /* Initialize test data */
+ in_buf_size = i_rand_limit(sizeof(in_buf));
+ for (j = 0; j < in_buf_size; j++)
+ in_buf[j] = i_rand_uchar();
+
+ /* Reset final output buffer */
+ buffer_set_used_size(out_buf, 0);
+
+ /* Determine line lengths */
+ chpl1 = i_rand_limit(30)*4;
+ chpl2 = i_rand_limit(30)*4;
+
+ /* Create stream for test data */
+ input1 = i_stream_create_from_data(in_buf, in_buf_size);
+ i_stream_set_name(input1, "[data]");
+
+ /* Determine which stages have sized streams */
+ sized_streams = i_rand_minmax(0x00, 0x0f);
+ /* Determine which stages use CRLF */
+ crlf_encode = i_rand_minmax(0x00, 0x03);
+
+ /* Create first encoder stream */
+ input2 = i_stream_create_base64_encoder(
+ input1, chpl1, HAS_ALL_BITS(crlf_encode, BIT(0)));
+ i_stream_set_name(input2, "[base64_encoder #1]");
+
+ if (HAS_ALL_BITS(sized_streams, BIT(0))) {
+ /* Wrap the first encoder stream in a sized stream to
+ check size and trigger any buffer overflow problems
+ */
+ base64_encode_init(&b64enc, &base64_scheme,
+ (HAS_ALL_BITS(crlf_encode, BIT(0)) ?
+ BASE64_ENCODE_FLAG_CRLF : 0),
+ chpl1);
+ sinput1 = i_stream_create_sized(input2,
+ base64_get_full_encoded_size(&b64enc,
+ in_buf_size));
+ i_stream_set_name(sinput1, "[sized #1]");
+ } else {
+ sinput1 = input2;
+ i_stream_ref(sinput1);
+ }
+
+ /* Create first decoder stream */
+ input3 = i_stream_create_base64_decoder(sinput1);
+ i_stream_set_name(input3, "[base64_decoder #1]");
+
+ if (HAS_ALL_BITS(sized_streams, BIT(1))) {
+ /* Wrap the first decoder stream in a sized stream to
+ check size and trigger any buffer overflow problems
+ */
+ sinput2 = i_stream_create_sized(input3, in_buf_size);
+ i_stream_set_name(sinput2, "[sized #2]");
+ } else {
+ sinput2 = input3;
+ i_stream_ref(sinput2);
+ }
+
+ /* Create second encoder stream */
+ input4 = i_stream_create_base64_encoder(
+ sinput2, chpl2, HAS_ALL_BITS(crlf_encode, BIT(1)));
+ i_stream_set_name(input4, "[base64_encoder #2]");
+
+ if (HAS_ALL_BITS(sized_streams, BIT(2))) {
+ /* Wrap the second encoder stream in a sized stream to
+ check size and trigger any buffer overflow problems
+ */
+ base64_encode_init(&b64enc, &base64_scheme,
+ (HAS_ALL_BITS(crlf_encode, BIT(1)) ?
+ BASE64_ENCODE_FLAG_CRLF : 0),
+ chpl2);
+ sinput3 = i_stream_create_sized(input4,
+ base64_get_full_encoded_size(&b64enc,
+ in_buf_size));
+ i_stream_set_name(sinput3, "[sized #3]");
+ } else {
+ sinput3 = input4;
+ i_stream_ref(sinput3);
+ }
+
+ /* Create second deoder stream */
+ input5 = i_stream_create_base64_decoder(sinput3);
+ i_stream_set_name(input5, "[base64_decoder #2]");
+
+ if (HAS_ALL_BITS(sized_streams, BIT(3))) {
+ /* Wrap the second decoder stream in a sized stream to
+ check size and trigger any buffer overflow problems
+ */
+ sinput4 = i_stream_create_sized(input5, in_buf_size);
+ i_stream_set_name(sinput4, "[sized #4]");
+ } else {
+ sinput4 = input5;
+ i_stream_ref(sinput4);
+ }
+
+
+ /* Assign random buffer sizes */
+ i_stream_set_max_buffer_size(input5, i_rand_minmax(1, 512));
+ i_stream_set_max_buffer_size(input4, i_rand_minmax(1, 512));
+ i_stream_set_max_buffer_size(input3, i_rand_minmax(1, 512));
+ i_stream_set_max_buffer_size(input2, i_rand_minmax(1, 512));
+
+ /* Read the outer stream in full with random increments. */
+ top_input = sinput4;
+ while ((ret = i_stream_read_more(
+ top_input, &data, &size)) > 0) {
+ size_t ch = i_rand_limit(512);
+
+ size = I_MIN(size, ch);
+ buffer_append(out_buf, data, size);
+ i_stream_skip(top_input, size);
+ }
+ if (ret < 0 && top_input->stream_errno == 0) {
+ data = i_stream_get_data(top_input, &size);
+ if (size > 0) {
+ buffer_append(out_buf, data, size);
+ i_stream_skip(top_input, size);
+ }
+ }
+
+ /* Assert stream status */
+ test_assert_idx(ret < 0 && top_input->stream_errno == 0, i);
+ /* Assert input/output equality */
+ test_assert_idx(out_buf->used == in_buf_size &&
+ memcmp(in_buf, out_buf->data, in_buf_size) == 0,
+ i);
+
+ if (top_input->stream_errno != 0) {
+ i_error("%s: %s", i_stream_get_name(input1),
+ i_stream_get_error(input1));
+ i_error("%s: %s", i_stream_get_name(input2),
+ i_stream_get_error(input2));
+ i_error("%s: %s", i_stream_get_name(input3),
+ i_stream_get_error(input3));
+ i_error("%s: %s", i_stream_get_name(input4),
+ i_stream_get_error(input4));
+ i_error("%s: %s", i_stream_get_name(input5),
+ i_stream_get_error(input5));
+ }
+
+ if (test_has_failed()) {
+ i_info("Test parameters: size=%zu "
+ "line_length_1=%u line_length_2=%u",
+ in_buf_size, chpl1, chpl2);
+ }
+
+ /* Clean up */
+ i_stream_unref(&input1);
+ i_stream_unref(&input2);
+ i_stream_unref(&input3);
+ i_stream_unref(&input4);
+ i_stream_unref(&input5);
+ i_stream_unref(&sinput1);
+ i_stream_unref(&sinput2);
+ i_stream_unref(&sinput3);
+ i_stream_unref(&sinput4);
+ }
+ test_end();
+}
+
+void test_istream_base64_decoder(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < N_ELEMENTS(base64_tests); i++) {
+ const struct base64_istream_test *test = &base64_tests[i];
+
+ test_begin(t_strdup_printf("istream base64 decoder %u", i+1));
+ decode_base64_test(test->input, test->output,
+ test->stream_errno);
+ test_end();
+ }
+
+ for (i = 0; i < N_ELEMENTS(base64url_tests); i++) {
+ const struct base64_istream_test *test = &base64url_tests[i];
+
+ test_begin(t_strdup_printf("istream base64url decoder %u",
+ i+1));
+ decode_base64url_test(test->input, test->output,
+ test->stream_errno);
+ test_end();
+ }
+
+ test_istream_base64_io_random();
+}