/* This software was written by Dirk Engling It is considered beerware. Prost. Skol. Cheers or whatever. $id$ */ /* System */ #include #include /* Opentracker */ #include "ot_hex.h" #include "test.h" static const uint8_t sample[20] = {0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xff, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0}; static void case_encode(void) { char dest[64]; test_case("a hash encodes to forty upper case digits and a terminator"); memset(dest, 0x7f, sizeof(dest)); CHECK_PTR(hex_fromhash(dest, sample), dest); CHECK_STR(dest, "000123456789ABCDEFFF10203040506070809" "0A0"); CHECK_INT(strlen(dest), 40); CHECK_INT((unsigned char)dest[41], 0x7f); } static void case_decode(void) { ot_hash hash; test_case("forty digits decode back to the hash"); CHECK(hex_tohash(hash, "000123456789ABCDEFFF10203040506070809" "0A0")); CHECK_MEM(hash, sample, sizeof(sample)); } static void case_round_trip(void) { ot_hash hash; char text[64]; size_t i; test_case("encode and decode are inverses over every byte value"); for (i = 0; i < 256; ++i) { uint8_t probe[20]; memset(probe, (uint8_t)i, sizeof(probe)); hex_fromhash(text, probe); if (!hex_tohash(hash, text) || memcmp(hash, probe, sizeof(probe))) { test_failf(__FILE__, __LINE__, "round trip failed for byte value %zu", i); return; } } ++test_checks; } static void case_decode_is_case_insensitive(void) { ot_hash upper, lower; test_case("lower case digits decode the same as upper case"); CHECK(hex_tohash(upper, "ABCDEF0123456789ABCDEF0123456789ABCDEF01")); CHECK(hex_tohash(lower, "abcdef0123456789abcdef0123456789abcdef01")); CHECK_MEM(lower, upper, sizeof(ot_hash)); } static void case_rejects_non_digits(void) { ot_hash hash; test_case("a non hex character anywhere rejects the whole string"); CHECK_INT(hex_tohash(hash, "g00123456789ABCDEFFF102030405060708090A0"), 0); CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF102030405060708090Ag"), 0); CHECK_INT(hex_tohash(hash, "000123456789ABCDEF:F102030405060708090A"), 0); } static void case_rejects_short_input(void) { ot_hash hash; test_case("a string shorter than forty digits is rejected"); CHECK_INT(hex_tohash(hash, ""), 0); CHECK_INT(hex_tohash(hash, "00"), 0); CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF102030405060708090A"), 0); } static void case_leaves_hash_untouched_on_failure(void) { ot_hash hash; test_case("a rejected string leaves the destination untouched"); memset(hash, 0x5a, sizeof(hash)); CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF10203040506070809XX0"), 0); CHECK_MEM(hash, "\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a", sizeof(hash)); } static void case_stops_at_the_terminator(void) { /* The saved state parser hands this whole buffers that may end early. The decoder must not read past the zero that stopped it. */ char guarded[8]; ot_hash hash; test_case("decoding stops at a terminator rather than running past it"); memcpy(guarded, "0012", 4); guarded[4] = 0; memset(guarded + 5, 'f', 3); CHECK_INT(hex_tohash(hash, guarded), 0); } static void case_isdigit(void) { test_case("hex_isdigit accepts exactly the hex digits"); CHECK(hex_isdigit('0')); CHECK(hex_isdigit('9')); CHECK(hex_isdigit('a')); CHECK(hex_isdigit('f')); CHECK(hex_isdigit('A')); CHECK(hex_isdigit('F')); CHECK_INT(hex_isdigit('g'), 0); CHECK_INT(hex_isdigit('G'), 0); CHECK_INT(hex_isdigit(':'), 0); CHECK_INT(hex_isdigit('\n'), 0); CHECK_INT(hex_isdigit(0), 0); CHECK_INT(hex_isdigit(0x80), 0); } void test_hex(void) { case_encode(); case_decode(); case_round_trip(); case_decode_is_case_insensitive(); case_rejects_non_digits(); case_rejects_short_input(); case_leaves_hash_untouched_on_failure(); case_stops_at_the_terminator(); case_isdigit(); }