From 0961e4b2e07324842de9d3f4c5fd5b677ae44d7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dag-Erling=20Sm=C3=B8rgrav?= Date: Fri, 28 Aug 2015 08:38:49 +0000 Subject: [PATCH] Rewrite unit tests for the Fletcher and Adler checksums to exercise boundary conditions (low and high word overflow). --- t/t_adler.c | 113 +++++++++++----------- t/t_fletcher.c | 255 ++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 257 insertions(+), 111 deletions(-) diff --git a/t/t_adler.c b/t/t_adler.c index f861e30..a7483c9 100644 --- a/t/t_adler.c +++ b/t/t_adler.c @@ -30,97 +30,95 @@ #include "cryb/impl.h" #include +#include #include +#include #include #include "t.h" +#define ADLER32_INVALID 0xffffffffLU + +#define TRI(n) (n * (n + 1) / 2) + +static uint8_t ones8[65536]; + struct t_case { const char *desc; - const char *data; + const void *data; size_t len; uint32_t sum; }; /*************************************************************************** * Test cases + * + * Most of these test cases use input sequences where each word has the + * value 1. To understand the test cases, it helps to realize that (until + * it overflows) the low word of the Adler checksum of such a sequence of + * length n is equal to n + 1, while the high word is equal to the nth + * triangular number plus n, i.e. (n*(n+1))/2+n. Also, unlike Fletcher32, + * the input width for Adler32 is always 8 bits. */ +#define TRIpN(n) ((n * (n + 1) / 2) + n) static struct t_case t_cases[] = { + /* trivial cases */ { - .desc = "len == 0", - .data = "\x01\x02\x03\x04\x05\x06\x07\x08", + .desc = "all-zeroes message", + .data = t_zero, + .len = sizeof t_zero, + .sum = sizeof t_zero << 16 | 0x0001U, + }, + { + .desc = "length 0", + .data = ones8, .len = 0, .sum = 0x00000001, }, + + /* overflow */ { - .desc = "len == 1", - .data = "\x01\x02\x03\x04\x05\x06\x07\x08", - .len = 1, - .sum = 0x00020002, + .desc = "high overflow - 1", + .data = ones8, + .len = 359, + .sum = (TRIpN(359U) % 65521U) << 16 | 360U, }, { - .desc = "len == 2", - .data = "\x01\x02\x03\x04\x05\x06\x07\x08", - .len = 2, - .sum = 0x00060004, + .desc = "high overflow", + .data = ones8, + .len = 363, + .sum = (TRIpN(363U) % 65521U) << 16 | 364U, }, { - .desc = "len == 3", - .data = "\x01\x02\x03\x04\x05\x06\x07\x08", - .len = 3, - .sum = 0x000d0007, + .desc = "low overflow - 1", + .data = ones8, + .len = 65519, + .sum = (TRIpN(65519U) % 65521U) << 16 | 65520U, }, { - .desc = "len == 4", - .data = "\x01\x02\x03\x04\x05\x06\x07\x08", - .len = 4, - .sum = 0x0018000b, + .desc = "low overflow", + .data = ones8, + .len = 65520, + .sum = (TRIpN(65520U) % 65521U) << 16 | 0U, }, { - .desc = "len == 5", - .data = "\x01\x02\x03\x04\x05\x06\x07\x08", - .len = 5, - .sum = 0x00280010, + .desc = "low overflow + 1", + .data = ones8, + .len = 65521, + .sum = ((TRIpN(65520U) + 1) % 65521U) << 16 | 1U, }, - { - .desc = "len == 6", - .data = "\x01\x02\x03\x04\x05\x06\x07\x08", - .len = 6, - .sum = 0x003e0016, - }, - { - .desc = "len == 7", - .data = "\x01\x02\x03\x04\x05\x06\x07\x08", - .len = 7, - .sum = 0x005b001d, - }, - { - .desc = "len == 8", - .data = "\x01\x02\x03\x04\x05\x06\x07\x08", - .len = 8, - .sum = 0x00800025, - }, - { - .desc = "zeroes", - .data = (const char *)t_zero, - .len = sizeof t_zero, - .sum = (sizeof t_zero << 16) | 1, - }, - /* XXX need non-zero cases which exercise modulo */ }; /*************************************************************************** * Test function */ static int -t_adler(char **desc CRYB_UNUSED, void *arg) +t_adler32(char **desc CRYB_UNUSED, void *arg) { struct t_case *t = arg; - int ret; - ret = t_compare_x32(t->sum, adler32_hash(t->data, t->len)); - return (ret); + return (t_compare_x32(t->sum, adler32_hash(t->data, t->len))); } @@ -131,13 +129,20 @@ t_adler(char **desc CRYB_UNUSED, void *arg) int t_prepare(int argc, char *argv[]) { - int i, n; + unsigned int i, n; (void)argc; (void)argv; + ones8[0] = 1; + be16enc(&ones16[0], 1); + for (i = 1; i < 65536; ++i) { + ones8[i] = ones8[0]; + ones16[i] = ones16[0]; + } n = sizeof t_cases / sizeof t_cases[0]; for (i = 0; i < n; ++i) - t_add_test(t_adler, &t_cases[i], t_cases[i].desc); + if (t_cases[i].sum != 0xffffffffLU) + t_add_test(t_adler32, &t_cases[i], t_cases[i].desc); return (0); } diff --git a/t/t_fletcher.c b/t/t_fletcher.c index 61c8837..36fe89b 100644 --- a/t/t_fletcher.c +++ b/t/t_fletcher.c @@ -30,15 +30,25 @@ #include "cryb/impl.h" #include +#include #include +#include #include #include "t.h" +#define FLETCHER16_INVALID 0xffffU +#define FLETCHER32_INVALID 0xffffffffLU +#define FLETCHER64_INVALID 0xffffffffffffffffLLU + +static uint8_t ones8[65536]; +static uint16_t ones16[65536]; +static uint32_t ones32[65536]; + struct t_case { const char *desc; - const char *data; + const void *data; size_t len; uint16_t sum16; uint32_t sum32; @@ -47,105 +57,221 @@ struct t_case { /*************************************************************************** * Test cases + * + * Most of these test cases use input sequences where each word has the + * value 1. To understand the test cases, it helps to realize that (until + * it overflows) the low word of the Fletcher checksum of such a sequence + * of length n is equal to n, while the high word is equal to the nth + * triangular number, i.e. (n*(n+1))/2+n. Also, remember that the input + * width for Fletcher is half the checksum width. */ +#define TRI(n) (n * (n + 1) / 2) static struct t_case t_cases[] = { + /* trivial cases */ { - .desc = "len == 0", - .data = "\x01\x02\x03\x04\x05\x06\x07\x08", - .len = 0, + .desc = "all-zeroes message", + .data = t_zero, + .len = sizeof t_zero, .sum16 = 0x00, .sum32 = 0x0000, .sum64 = 0x00000000, }, { - .desc = "len == 1", - .data = "\x01\x02\x03\x04\x05\x06\x07\x08", + .desc = "length 0", + .data = ones8, + .len = 0, + .sum16 = 0x00, + .sum32 = 0x0000, + .sum64 = 0x00000000, + }, + + /* padding */ + { + .desc = "length 1", + .data = ones8, .len = 1, .sum16 = 0x0101, .sum32 = 0x01000100, .sum64 = 0x0100000001000000, }, { - .desc = "len == 2", - .data = "\x01\x02\x03\x04\x05\x06\x07\x08", + .desc = "length 2", + .data = ones8, .len = 2, - .sum16 = 0x0403, - .sum32 = 0x01020102, - .sum64 = 0x0102000001020000, + .sum16 = 0x0302, + .sum32 = 0x01010101, + .sum64 = 0x0101000001010000, }, { - .desc = "len == 3", - .data = "\x01\x02\x03\x04\x05\x06\x07\x08", + .desc = "length 3", + .data = ones8, .len = 3, - .sum16 = 0x0a06, - .sum32 = 0x05040402, - .sum64 = 0x102030001020300, + .sum16 = 0x0603, + .sum32 = 0x03020201, + .sum64 = 0x0101010001010100, }, { - .desc = "len == 4", - .data = "\x01\x02\x03\x04\x05\x06\x07\x08", + .desc = "length 4", + .data = ones8, .len = 4, - .sum16 = 0x140a, - .sum32 = 0x05080406, - .sum64 = 0x0102030401020304, + .sum16 = 0x0a04, + .sum32 = 0x03030202, + .sum64 = 0x0101010101010101, }, { - .desc = "len == 5", - .data = "\x01\x02\x03\x04\x05\x06\x07\x08", + .desc = "length 5", + .data = ones8, .len = 5, - .sum16 = 0x230f, - .sum32 = 0x0e0e0906, - .sum64 = 0x0704060806020304, + .sum16 = 0x0f05, + .sum32 = 0x06050302, + .sum64 = 0x0302020202010101, }, { - .desc = "len == 6", - .data = "\x01\x02\x03\x04\x05\x06\x07\x08", + .desc = "length 6", + .data = ones8, .len = 6, - .sum16 = 0x3815, - .sum32 = 0x0e14090c, - .sum64 = 0x070a060806080304, + .sum16 = 0x1506, + .sum32 = 0x06060303, + .sum64 = 0x0303020202020101, }, { - .desc = "len == 7", - .data = "\x01\x02\x03\x04\x05\x06\x07\x08", + .desc = "length 7", + .data = ones8, .len = 7, - .sum16 = 0x541c, - .sum32 = 0x1e20100c, - .sum64 = 0x070a0d0806080a04, + .sum16 = 0x1c07, + .sum32 = 0x0a090403, + .sum64 = 0x0303030202020201, }, { - .desc = "len == 8", - .data = "\x01\x02\x03\x04\x05\x06\x07\x08", + .desc = "length 8", + .data = ones8, .len = 8, - .sum16 = 0x7824, - .sum32 = 0x1e281014, - .sum64 = 0x070a0d1006080a0c, + .sum16 = 0x2408, + .sum32 = 0x0a0a0404, + .sum64 = 0x0303030302020202, }, - /* not really useful... */ + + /* overflow (16-bit checksum) */ { - .desc = "zeroes", - .data = (const char *)t_zero, - .len = sizeof t_zero, - .sum16 = 0x00, - .sum32 = 0x0000, - .sum64 = 0x00000000, + .desc = "high overflow - 1", + .data = ones8, + .len = 22, + .sum16 = (TRI(22) % 255) << 8 | 22, + .sum32 = FLETCHER32_INVALID, + .sum64 = FLETCHER64_INVALID, }, - /* XXX need non-zero cases which exercise modulo */ + { + .desc = "high overflow", + .data = ones8, + .len = 23, + .sum16 = (TRI(23) % 255) << 8 | 23, + .sum32 = FLETCHER32_INVALID, + .sum64 = FLETCHER64_INVALID, + }, + { + .desc = "low overflow - 1", + .data = ones8, + .len = 254, + .sum16 = (TRI(254) % 255) << 8 | 254, + .sum32 = FLETCHER32_INVALID, + .sum64 = FLETCHER64_INVALID, + }, + { + .desc = "low overflow", + .data = ones8, + .len = 255, + .sum16 = (TRI(254) % 255) << 8 | 0, + .sum32 = FLETCHER32_INVALID, + .sum64 = FLETCHER64_INVALID, + }, + { + .desc = "low overflow + 1", + .data = ones8, + .len = 256, + .sum16 = ((TRI(254) + 1) % 255) << 8 | 1, + .sum32 = FLETCHER32_INVALID, + .sum64 = FLETCHER64_INVALID, + }, + + /* overflow (32-bit checksum) */ + { + .desc = "high overflow - 1", + .data = ones16, + .len = 362 * sizeof ones16[0], + .sum16 = FLETCHER16_INVALID, + .sum32 = (TRI(362U) % 65535U) << 16 | 362U, + .sum64 = FLETCHER64_INVALID, + }, + { + .desc = "high overflow", + .data = ones16, + .len = 363 * sizeof ones16[0], + .sum16 = FLETCHER16_INVALID, + .sum32 = (TRI(363U) % 65535U) << 16 | 363U, + .sum64 = FLETCHER64_INVALID, + }, + { + .desc = "low overflow - 1", + .data = ones16, + .len = 65534 * sizeof ones16[0], + .sum16 = FLETCHER16_INVALID, + .sum32 = (TRI(65534U) % 65535U) << 16 | 65534U, + .sum64 = FLETCHER64_INVALID, + }, + { + .desc = "low overflow", + .data = ones16, + .len = 65535 * sizeof ones16[0], + .sum16 = FLETCHER16_INVALID, + .sum32 = (TRI(65534U) % 65535U) << 16 | 0U, + .sum64 = FLETCHER64_INVALID, + }, + { + .desc = "low overflow + 1", + .data = ones16, + .len = 65536 * sizeof ones16[0], + .sum16 = FLETCHER16_INVALID, + .sum32 = ((TRI(65534U) + 1) % 65535U) << 16 | 1U, + .sum64 = FLETCHER64_INVALID, + }, + + /* overflow (64-bit checksum */ + /* + * This is currently not realistic. We need to change the + * fletcher*() API to take a seed so it can do partial / + * incremental checksums, and provide a precomputed seed just + * below the overflow point. + */ }; /*************************************************************************** * Test function */ static int -t_fletcher(char **desc CRYB_UNUSED, void *arg) +t_fletcher16(char **desc, void *arg) { struct t_case *t = arg; - int ret; - ret = t_compare_x16(t->sum16, fletcher16_hash(t->data, t->len)) & - t_compare_x32(t->sum32, fletcher32_hash(t->data, t->len)) & - t_compare_x64(t->sum64, fletcher64_hash(t->data, t->len)); - return (ret); + asprintf(desc, "(16-bit) %s", t->desc); + return (t_compare_x16(t->sum16, fletcher16_hash(t->data, t->len))); +} + +static int +t_fletcher32(char **desc, void *arg) +{ + struct t_case *t = arg; + + asprintf(desc, "(32-bit) %s", t->desc); + return (t_compare_x32(t->sum32, fletcher32_hash(t->data, t->len))); +} + +static int +t_fletcher64(char **desc, void *arg) +{ + struct t_case *t = arg; + + asprintf(desc, "(64-bit) %s", t->desc); + return (t_compare_x64(t->sum64, fletcher64_hash(t->data, t->len))); } @@ -156,13 +282,28 @@ t_fletcher(char **desc CRYB_UNUSED, void *arg) int t_prepare(int argc, char *argv[]) { - int i, n; + unsigned int i, n; (void)argc; (void)argv; + ones8[0] = 1; + be16enc(&ones16[0], 1); + be32enc(&ones32[0], 1); + for (i = 1; i < 65536; ++i) { + ones8[i] = ones8[0]; + ones16[i] = ones16[0]; + ones32[i] = ones32[0]; + } n = sizeof t_cases / sizeof t_cases[0]; for (i = 0; i < n; ++i) - t_add_test(t_fletcher, &t_cases[i], t_cases[i].desc); + if (t_cases[i].sum16 != 0xffffU) + t_add_test(t_fletcher16, &t_cases[i], t_cases[i].desc); + for (i = 0; i < n; ++i) + if (t_cases[i].sum32 != 0xffffffffLU) + t_add_test(t_fletcher32, &t_cases[i], t_cases[i].desc); + for (i = 0; i < n; ++i) + if (t_cases[i].sum64 != 0xffffffffffffffffLLU) + t_add_test(t_fletcher64, &t_cases[i], t_cases[i].desc); return (0); }