/* vim: set ts=8 sts=4 sw=4 tw=80 noet: */
/*======================================================================
Copyright (C) 2004,2005,2009 Walter Doekes <walter+tthsum@wjd.nu>
This file is part of tthsum.

tthsum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

tthsum 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 a copy of the GNU General Public License
along with tthsum.  If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#include "base32.h"

#include "test.h"
#include <string.h>

#ifdef USE_TEXTS
#   include "texts.h"
#else /* !USE_TEXTS */
#   define get_error() "Fail"
#endif /* !USE_TEXTS */


static int help_cmp_symmetric8(const char* name, const uint8_t* uint8,
	unsigned uint8len, const char* base32, int should_fail) {
    uint8_t uint8buf[2048];
    char base32buf[2048];
    memset(uint8buf, 0, 2048);
    memset(base32buf, 85, 2048);

    if (uint8len >= 1024 || strlen(base32) >= 2048)
	FAIL3("Test values too large: \"%s\" is %u and %u bytes", name,
		uint8len, (unsigned)strlen(base32));
    if (uint8tobase32(base32buf, uint8, uint8len) != 0)
	FAIL2("uint8tobase32 failed on \"%s\": %s", name, get_error());
    if (base32touint8(uint8buf, base32, uint8len) != 0)
	FAIL2("base32touint8 failed on \"%s\": %s", name, get_error());
    if ((memcmp(uint8, uint8buf, uint8len) != 0
	    || strcmp(base32, base32buf) != 0) == !should_fail)
	FAIL2("Values %sdiffer for \"%s\"", should_fail ? "do NOT " : "", name);

    return 0;
}

static int help_cmp_symmetric64(const char* name, const uint64_t* uint64,
	unsigned uint64len, const char* base32, int should_fail) {
    uint64_t uint64buf[2048];
    char base32buf[2048];
    memset(uint64buf, 0, 2048);
    memset(base32buf, 85, 2048);

    if (uint64len >= 1024 / 8 || strlen(base32) >= 2048)
	FAIL3("Test values too large: \"%s\" is %u*8 and %u bytes", name,
		uint64len, (unsigned)strlen(base32));
    if (uint64tobase32(base32buf, uint64, uint64len) != 0)
	FAIL2("uint64tobase32 failed on \"%s\": %s", name, get_error());
    if (base32touint64(uint64buf, base32, uint64len) != 0)
	FAIL2("base32touint64 failed on \"%s\": %s", name, get_error());
    if ((memcmp(uint64, uint64buf, uint64len) != 0
	    || strcmp(base32, base32buf) != 0) == !should_fail)
	FAIL2("Values %sdiffer for \"%s\"", should_fail ? "do NOT " : "", name);

    return 0;
}

static int help_cmp_symmetric(const char* string, const char* base32) {
    return help_cmp_symmetric8(string, (const uint8_t*)string,
	    strlen(string), base32, 0);
}

static int help_not_cmp_symmetric(const char* string, const char* base32) {
    return help_cmp_symmetric8(string, (const uint8_t*)string,
	    strlen(string), base32, 1);
}

static int help_cmp_decode8(const char* name, const char* base32,
	const uint8_t* uint8, unsigned uint8len, int should_fail) {
    uint8_t uint8buf[2048];
    memset(uint8buf, 0, 2048);

    if (uint8len > 1024)
	FAIL2("Test value too large: \"%s\" is %u bytes", name, uint8len);
    if (base32touint8(uint8buf, base32, uint8len) != 0)
	FAIL1("base32touint8 failed on \"%s\"", name);
    if (memcmp(uint8, uint8buf, uint8len) == !should_fail)
	FAIL2("Values %sdiffer for \"%s\"", should_fail ? "do NOT " : "", name);

    return 0;
}

static int help_cmp_decode(const char* base32, const char* string) {
    return help_cmp_decode8(base32, base32, (const uint8_t*)string,
	    strlen(string), 0);
}

static int test_bidirectional_rfc4648() {
    return help_cmp_symmetric("", "")
	    + help_cmp_symmetric("f", "MY")
	    + help_cmp_symmetric("fo", "MZXQ")
	    + help_cmp_symmetric("foo", "MZXW6")
	    + help_cmp_symmetric("foob", "MZXW6YQ")
	    + help_cmp_symmetric("fooba", "MZXW6YTB")
	    + help_cmp_symmetric("foobar", "MZXW6YTBOI");
}

static int test_not_bidirectional_rfc4648() {
    return help_not_cmp_symmetric("", "a")
	    + help_not_cmp_symmetric("g", "MY")
	    + help_not_cmp_symmetric("go", "MZXQ")
	    + help_not_cmp_symmetric("goo", "MZXW6")
	    + help_not_cmp_symmetric("fooB", "MZXW6YQ")
	    + help_not_cmp_symmetric("fooBa", "MZXW6YTB")
	    + help_not_cmp_symmetric("fooBar", "MZXW6YTBOI")
	    + help_not_cmp_symmetric("fooba", "MZXW6YTBOI")
	    + help_not_cmp_symmetric("foob", "MZXW6YTB")
	    + help_not_cmp_symmetric("foo", "MZXW6YQ");
}

static int test_unidirectional_rfc4648() {
   return help_cmp_decode("====", "")
	    + help_cmp_decode("my====", "f")
	    + help_cmp_decode("mY", "f")
	    + help_cmp_decode("mZxW6yTbOi====", "foobar");
}

static int test_decode_success() {
    uint8_t buf[256];
    return base32touint8(buf, "AA", 1)
	|| base32touint8(buf, "77", 1)
	|| base32touint8(buf, "777", 1)
	|| base32touint8(buf, "7777", 2)
	|| base32touint8(buf, "77777", 3)
	|| base32touint8(buf, "AAAAAA", 3)
	|| base32touint8(buf, "CCCCCCC", 4)
	|| base32touint8(buf, "BBBBBBBB", 5)
	|| base32touint8(buf, "ABCDEFGH", 5);
}

static int test_decode_fail() {
    uint8_t buf[256];
    return !base32touint8(buf, "A", 1)
	|| !base32touint8(buf, "77", 2)
	|| !base32touint8(buf, "ABCDEFGH", 6);
}

#define C(val) ((const uint8_t*)val)
static int test_misc_binary() {
    return help_cmp_symmetric8("\\0", C("\0"), 1, "AA", 0)
	    + help_cmp_symmetric8("\\0\\0", C("\0\0"), 2, "AAAA", 0)
	    + help_cmp_symmetric8("\\xff", C("\xff"), 1, "74", 0)
	    + help_cmp_symmetric8("\\xff\\0\\xff", C("\xff\0\xff"), 3,
		    "74AP6", 0);
}
#undef C

static int test_misc_binary64() {
    int ret = 0;
    uint64_t buf[16];
    buf[0] = _ULL(0x8b630e030ad09e5d);
    buf[1] = _ULL(0x0e90fb246a3a75db);
    buf[2] = _ULL(0xb6256c3ee7b8635a);
    ret += help_cmp_symmetric64("test64:1", buf, 3,
	    "LWPNACQDBZRYXW3VHJVCJ64QBZNGHOHHHZWCLNQ", 0);
    ++buf[2];
    ret += help_cmp_symmetric64("test64:2", buf, 3,
	    "LWPNACQDBZRYXW3VHJVCJ64QBZNGHOHHHZWCLNQ", 1);
    return ret;
}


TESTS(base32_test)
    TEST(test_bidirectional_rfc4648);
    TEST(test_not_bidirectional_rfc4648);
    TEST(test_unidirectional_rfc4648);
    TEST(test_decode_success);
    TEST(test_decode_fail);
    TEST(test_misc_binary);
    TEST(test_misc_binary64);
ENDTESTS
