summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sha3.c502
1 files changed, 501 insertions, 1 deletions
diff --git a/sha3.c b/sha3.c
index 88c83cb..b0ab709 100644
--- a/sha3.c
+++ b/sha3.c
@@ -869,6 +869,74 @@ static inline size_t right_encode(uint8_t buf[static 9], const uint64_t n) {
}
}
+// kangarootwelve utility function
+// (similar to right_encode(), but slightly different)
+static inline size_t kangarootwelve_length_encode(uint8_t buf[static 9], const uint64_t n) {
+ if (n > 0x00ffffffffffffffULL) {
+ buf[0] = (n >> 56) & 0xff;
+ buf[1] = (n >> 40) & 0xff;
+ buf[2] = (n >> 32) & 0xff;
+ buf[3] = (n >> 24) & 0xff;
+ buf[4] = (n >> 16) & 0xff;
+ buf[5] = (n >> 8) & 0xff;
+ buf[6] = n & 0xff;
+ buf[7] = 8;
+ return 9;
+ } else if (n > 0x0000ffffffffffffULL) {
+ buf[0] = (n >> 56) & 0xff;
+ buf[1] = (n >> 40) & 0xff;
+ buf[2] = (n >> 32) & 0xff;
+ buf[3] = (n >> 24) & 0xff;
+ buf[4] = (n >> 16) & 0xff;
+ buf[5] = (n >> 8) & 0xff;
+ buf[6] = n & 0xff;
+ buf[7] = 7;
+ return 8;
+ } else if (n > 0x000000ffffffffffULL) {
+ buf[0] = (n >> 40) & 0xff;
+ buf[1] = (n >> 32) & 0xff;
+ buf[2] = (n >> 24) & 0xff;
+ buf[3] = (n >> 16) & 0xff;
+ buf[4] = (n >> 8) & 0xff;
+ buf[5] = n & 0xff;
+ buf[6] = 6;
+ return 7;
+ } else if (n > 0x00000000ffffffffULL) {
+ buf[0] = (n >> 32) & 0xff;
+ buf[1] = (n >> 24) & 0xff;
+ buf[2] = (n >> 16) & 0xff;
+ buf[3] = (n >> 8) & 0xff;
+ buf[4] = n & 0xff;
+ buf[5] = 5;
+ return 6;
+ } else if (n > 0x0000000000ffffffULL) {
+ buf[0] = (n >> 24) & 0xff;
+ buf[1] = (n >> 16) & 0xff;
+ buf[2] = (n >> 8) & 0xff;
+ buf[3] = n & 0xff;
+ buf[4] = 4;
+ return 5;
+ } else if (n > 0x000000000000ffffULL) {
+ buf[0] = (n >> 16) & 0xff;
+ buf[1] = (n >> 8) & 0xff;
+ buf[2] = n & 0xff;
+ buf[3] = 3;
+ return 4;
+ } else if (n > 0x00000000000000ffULL) {
+ buf[0] = (n >> 8) & 0xff;
+ buf[1] = n & 0xff;
+ buf[2] = 2;
+ return 3;
+ } else if (n > 0) {
+ buf[0] = n & 0xff;
+ buf[1] = 1;
+ return 2;
+ } else {
+ buf[0] = 0;
+ return 1;
+ }
+}
+
// Write prefix for encode_string() to the given buffer. Accepts the
// length of the string, in bytes.
//
@@ -1764,8 +1832,181 @@ void turboshake256_custom(const uint8_t pad, const uint8_t * const src, const si
xof_once(SHAKE256_XOF_RATE, TURBOSHAKE_NUM_ROUNDS, pad, src, src_len, dst, dst_len);
}
+// kangarootwelve block size, in bytes
+#define KT_BLOCK_LEN 8192
+
+// pad byte for single kangarootwelve chunk (<= 8192 bytes)
+#define KT_PAD_SINGLE 0x07
+
+// pad byte for root kangarootwelve turboshake instance (> 8192 bytes)
+#define KT_PAD_ROOT 0x06
+
+// pad byte for child kangarootwelve turboshake instances (> 8192 bytes)
+#define KT_PAD_CHILD 0x0B
+
+// private kangarootwelve context
+typedef struct {
+ turboshake_t root, // root turboshake context
+ curr; // current child turboshake context
+ size_t num_bytes, // num bytes in current block
+ num_blocks; // total number of blocks
+} kangarootwelve_t;
+
+// init kangarootwelve context
+static void kangarootwelve_init(kangarootwelve_t * const kt) {
+ turboshake128_init_custom(&(kt->root), KT_PAD_ROOT);
+ kt->num_bytes = 0;
+ kt->num_blocks = 0;
+}
+
+// absorb data in child context
+static void kangarootwelve_child_absorb(kangarootwelve_t * const kt, const uint8_t *src, size_t src_len) {
+ while (src_len > 0) {
+ const size_t len = MIN(KT_BLOCK_LEN - kt->num_bytes, src_len);
+
+ // absorb into child context
+ turboshake128_absorb(&(kt->curr), src, len);
+
+ src += len;
+ src_len -= len;
+ kt->num_bytes += len;
+
+ if (kt->num_bytes == KT_BLOCK_LEN) {
+ // hash child
+ uint8_t buf[32] = { 0 };
+ turboshake128_squeeze(&(kt->curr), buf, sizeof(buf));
+
+ // absorb hash into root
+ turboshake128_absorb(&(kt->root), buf, sizeof(buf));
+
+ // reset child
+ turboshake128_init_custom(&(kt->curr), KT_PAD_CHILD);
+
+ // clear byte count, increment block count
+ kt->num_bytes = 0;
+ kt->num_blocks++;
+ }
+ }
+}
+
+// absorb data in root context
+// (passes excess data to child context)
+static void kangarootwelve_root_absorb(kangarootwelve_t * const kt, const uint8_t *src, size_t src_len) {
+ while (src_len > 0) {
+ const size_t len = MIN(KT_BLOCK_LEN - kt->num_bytes, src_len);
+
+ // absorb into root context
+ turboshake128_absorb(&(kt->root), src, len);
+
+ src += len;
+ src_len -= len;
+ kt->num_bytes += len;
+
+ if (kt->num_bytes == KT_BLOCK_LEN) {
+ // absorb trailer for first block
+ uint8_t buf[8] = { 3, 0, 0, 0, 0, 0, 0, 0 };
+ turboshake128_absorb(&(kt->root), buf, sizeof(buf));
+
+ // init child
+ turboshake128_init_custom(&(kt->curr), KT_PAD_CHILD);
+
+ // clear byte count, increment block count
+ kt->num_bytes = 0;
+ kt->num_blocks++;
+
+ // absorb rest of source in child
+ kangarootwelve_child_absorb(kt, src, src_len);
+ return;
+ }
+ }
+}
+
+// absorb data
+static void kangarootwelve_absorb(kangarootwelve_t * const kt, const uint8_t *src, size_t src_len) {
+ if (kt->num_blocks) {
+ // absorb successive blocks in child context
+ kangarootwelve_child_absorb(kt, src, src_len);
+ } else {
+ // absorb first block in root context
+ kangarootwelve_root_absorb(kt, src, src_len);
+ }
+}
+
+// finalize kangarootwelve context and squeeze data into destination
+static void kangarootwelve_squeeze(kangarootwelve_t * const kt, uint8_t *dst, const size_t dst_len) {
+ if (kt->num_bytes > 0) {
+ // hash child, absorb into root
+ uint8_t buf[32] = { 0 };
+ turboshake128_squeeze(&(kt->curr), buf, sizeof(buf));
+ turboshake128_absorb(&(kt->root), buf, sizeof(buf));
+ }
+
+ // absorb block count
+ uint8_t buf[9];
+ const size_t buf_len = kangarootwelve_length_encode(buf, kt->num_blocks);
+ turboshake128_absorb(&(kt->root), buf, buf_len);
+
+ // absorb tail
+ static const uint8_t tail[2] = { 0xff, 0xff };
+ turboshake128_absorb(&(kt->root), tail, sizeof(tail));
+
+ // squeeze
+ turboshake128_squeeze(&(kt->root), dst, dst_len);
+}
+
+// one-shot kangarootwelve with custom string
+void kangarootwelve_custom(const uint8_t *custom, const size_t custom_len, const uint8_t *src, const size_t src_len, uint8_t *dst, const size_t dst_len) {
+ uint8_t cl_buf[9] = { 0 };
+ const size_t cl_buf_len = kangarootwelve_length_encode(cl_buf, custom_len);
+
+ // get total size, in bytes
+ const size_t total_len = src_len + custom_len + cl_buf_len;
+ if (total_len <= KT_BLOCK_LEN) {
+ // total size is less than a single block, so create a single
+ // turboshake128 instance, absorb the source data and the custom
+ // string, then squeeze into the destination buffer
+
+ // init
+ turboshake_t ts;
+ turboshake128_init_custom(&ts, KT_PAD_SINGLE);
+
+ // absorb source, custom string, and custom string length
+ turboshake128_absorb(&ts, src, src_len);
+ turboshake128_absorb(&ts, custom, custom_len);
+ turboshake128_absorb(&ts, cl_buf, cl_buf_len);
+
+ // squeeze into destination
+ turboshake128_squeeze(&ts, dst, dst_len);
+ } else {
+ // total size greater than a single block, so create an internal
+ // kangarootwelve context, absorb the source data and the custom
+ // string into the context, then squeeze into the destination buffer
+ //
+ // (the internal kangarootwelve context takes care of multiplexing
+ // the block data between the root and child contexts)
+
+ // init
+ kangarootwelve_t kt;
+ kangarootwelve_init(&kt);
+
+ // absorb source, custom string, and custom string length
+ kangarootwelve_absorb(&kt, src, src_len);
+ kangarootwelve_absorb(&kt, custom, custom_len);
+ kangarootwelve_absorb(&kt, cl_buf, cl_buf_len);
+
+ // squeeze into destination
+ kangarootwelve_squeeze(&kt, dst, dst_len);
+ }
+}
+
+// one-shot kangarootwelve w/o custom string
+void kangarootwelve(const uint8_t *src, const size_t src_len, uint8_t *dst, const size_t dst_len) {
+ kangarootwelve_custom(NULL, 0, src, src_len, dst, dst_len);
+}
+
#ifdef SHA3_TEST
#include <stdio.h> // printf()
+#include <stdlib.h> // malloc() (used in test_kangarootwelve())
static void dump_hex(FILE *f, const uint8_t * const a, const size_t len) {
fprintf(f, " ");
@@ -6215,7 +6456,7 @@ static void test_turboshake256(void) {
for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
// init
- turboshake_t ts;
+ turboshake_t ts = { 0 };
turboshake256_init_custom(&ts, tests[i].pad);
// absorb
@@ -6239,6 +6480,263 @@ static void test_turboshake256(void) {
}
}
+static void test_kangarootwelve_length_encode(void) {
+ static const struct {
+ const char *name;
+ uint64_t val;
+ uint8_t exp[9];
+ size_t exp_len;
+ } tests[] = {{
+ .name = "zero",
+ .val = 0,
+ .exp = { 0x00 },
+ .exp_len = 1,
+ }, {
+ .name = "1",
+ .val = 1,
+ .exp = { 0x01, 0x01 },
+ .exp_len = 2,
+ }, {
+ .name = "120",
+ .val = 120,
+ .exp = { 0x78, 0x01 },
+ .exp_len = 2,
+ }, {
+ .name = "256",
+ .val = 256,
+ .exp = { 0x01, 0x00, 0x02 },
+ .exp_len = 3,
+ }, {
+ .name = "65535",
+ .val = 65535,
+ .exp = { 0xff, 0xff, 0x02 },
+ .exp_len = 3,
+ }, {
+ .name = "65536",
+ .val = 65536,
+ .exp = { 0x01, 0x00, 0x00, 0x03 },
+ .exp_len = 4,
+ }, {
+ .name = "0xff00ff",
+ .val = 0xff00ff,
+ .exp = { 0xff, 0x00, 0xff, 0x03 },
+ .exp_len = 4,
+ }, {
+ .name = "0x01000000",
+ .val = 0x01000000,
+ .exp = { 0x01, 0x00, 0x00, 0x00, 0x04 },
+ .exp_len = 5,
+ }, {
+ .name = "0xffffffff",
+ .val = 0xffffffff,
+ .exp = { 0xff, 0xff, 0xff, 0xff, 0x04 },
+ .exp_len = 5,
+ }, {
+ .name = "0x0100000000",
+ .val = 0x0100000000,
+ .exp = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x05 },
+ .exp_len = 6,
+ }};
+
+ for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+ uint8_t got[9];
+ const size_t got_len = kangarootwelve_length_encode(got, tests[i].val);
+
+ // check length and data
+ if (got_len != tests[i].exp_len) {
+ fprintf(stderr, "test_kangarootwelve_length_encode(\"%s\") length check failed: got %zu, exp %zu:\n", tests[i].name, got_len, tests[i].exp_len);
+ } else if (memcmp(got, tests[i].exp, got_len)) {
+ fprintf(stderr, "test_kangarootwelve_length_encode(\"%s\") failed, got:\n", tests[i].name);
+ dump_hex(stderr, got, got_len);
+
+ fprintf(stderr, "exp:\n");
+ dump_hex(stderr, tests[i].exp, got_len);
+ }
+ }
+}
+
+static void test_kangarootwelve(void) {
+ // test pattern
+ // src: https://www.ietf.org/archive/id/draft-irtf-cfrg-kangarootwelve-10.html#name-test-vectors
+ static const uint8_t PATTERN[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
+ 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
+ 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
+ 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
+ 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
+ 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
+ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA,
+ };
+
+ static const struct {
+ const char *name; // test name
+ const bool is_data; // true: message is from data field, false: message is from pattern
+ uint8_t data[10]; // message data (if is_data == true)
+ const size_t len; // message length, in bytes
+ const size_t custom_len; // custom length, in bytes (data itself comes from PATTERN)
+ const uint8_t exp[32]; // expected hash
+ } tests[] = {{
+ .name = "len=0, c=0",
+ .len = 0,
+ .custom_len = 0,
+ .exp = {
+ 0x1A, 0xC2, 0xD4, 0x50, 0xFC, 0x3B, 0x42, 0x05, 0xD1, 0x9D, 0xA7, 0xBF, 0xCA, 0x1B, 0x37, 0x51,
+ 0x3C, 0x08, 0x03, 0x57, 0x7A, 0xC7, 0x16, 0x7F, 0x06, 0xFE, 0x2C, 0xE1, 0xF0, 0xEF, 0x39, 0xE5,
+ },
+ }, {
+ .name = "len=1, c=0",
+ .len = 1,
+ .custom_len = 0,
+ .exp = {
+ 0x2B, 0xDA, 0x92, 0x45, 0x0E, 0x8B, 0x14, 0x7F, 0x8A, 0x7C, 0xB6, 0x29, 0xE7, 0x84, 0xA0, 0x58,
+ 0xEF, 0xCA, 0x7C, 0xF7, 0xD8, 0x21, 0x8E, 0x02, 0xD3, 0x45, 0xDF, 0xAA, 0x65, 0x24, 0x4A, 0x1F,
+ },
+ }, {
+ .name = "len=17, c=0",
+ .len = 17,
+ .custom_len = 0,
+ .exp = {
+ 0x6B, 0xF7, 0x5F, 0xA2, 0x23, 0x91, 0x98, 0xDB, 0x47, 0x72, 0xE3, 0x64, 0x78, 0xF8, 0xE1, 0x9B,
+ 0x0F, 0x37, 0x12, 0x05, 0xF6, 0xA9, 0xA9, 0x3A, 0x27, 0x3F, 0x51, 0xDF, 0x37, 0x12, 0x28, 0x88,
+ },
+ }, {
+ .name = "len=17**2, c=0",
+ .len = 17*17,
+ .custom_len = 0,
+ .exp = {
+ 0x0C, 0x31, 0x5E, 0xBC, 0xDE, 0xDB, 0xF6, 0x14, 0x26, 0xDE, 0x7D, 0xCF, 0x8F, 0xB7, 0x25, 0xD1,
+ 0xE7, 0x46, 0x75, 0xD7, 0xF5, 0x32, 0x7A, 0x50, 0x67, 0xF3, 0x67, 0xB1, 0x08, 0xEC, 0xB6, 0x7C,
+ },
+ }, {
+ .name = "len=17**3, c=0",
+ .len = 17*17*17,
+ .custom_len = 0,
+ .exp = {
+ 0xCB, 0x55, 0x2E, 0x2E, 0xC7, 0x7D, 0x99, 0x10, 0x70, 0x1D, 0x57, 0x8B, 0x45, 0x7D, 0xDF, 0x77,
+ 0x2C, 0x12, 0xE3, 0x22, 0xE4, 0xEE, 0x7F, 0xE4, 0x17, 0xF9, 0x2C, 0x75, 0x8F, 0x0D, 0x59, 0xD0,
+ },
+ }, {
+ .name = "len=17**4, c=0",
+ .len = 17*17*17*17,
+ .custom_len = 0,
+ .exp = {
+ 0x87, 0x01, 0x04, 0x5E, 0x22, 0x20, 0x53, 0x45, 0xFF, 0x4D, 0xDA, 0x05, 0x55, 0x5C, 0xBB, 0x5C,
+ 0x3A, 0xF1, 0xA7, 0x71, 0xC2, 0xB8, 0x9B, 0xAE, 0xF3, 0x7D, 0xB4, 0x3D, 0x99, 0x98, 0xB9, 0xFE,
+ },
+ }, {
+ .name = "len=17**5, c=0",
+ .len = 17*17*17*17*17,
+ .custom_len = 0,
+ .exp = {
+ 0x84, 0x4D, 0x61, 0x09, 0x33, 0xB1, 0xB9, 0x96, 0x3C, 0xBD, 0xEB, 0x5A, 0xE3, 0xB6, 0xB0, 0x5C,
+ 0xC7, 0xCB, 0xD6, 0x7C, 0xEE, 0xDF, 0x88, 0x3E, 0xB6, 0x78, 0xA0, 0xA8, 0xE0, 0x37, 0x16, 0x82,
+ },
+ }, {
+ .name = "len=17**6, c=0",
+ .len = 17*17*17*17*17*17,
+ .custom_len = 0,
+ .exp = {
+ 0x3C, 0x39, 0x07, 0x82, 0xA8, 0xA4, 0xE8, 0x9F, 0xA6, 0x36, 0x7F, 0x72, 0xFE, 0xAA, 0xF1, 0x32,
+ 0x55, 0xC8, 0xD9, 0x58, 0x78, 0x48, 0x1D, 0x3C, 0xD8, 0xCE, 0x85, 0xF5, 0x8E, 0x88, 0x0A, 0xF8,
+ },
+ }, {
+ .name = "len=0, c=1",
+ .is_data = true,
+ .len = 0,
+ .custom_len = 1,
+ .exp = {
+ 0xFA, 0xB6, 0x58, 0xDB, 0x63, 0xE9, 0x4A, 0x24, 0x61, 0x88, 0xBF, 0x7A, 0xF6, 0x9A, 0x13, 0x30,
+ 0x45, 0xF4, 0x6E, 0xE9, 0x84, 0xC5, 0x6E, 0x3C, 0x33, 0x28, 0xCA, 0xAF, 0x1A, 0xA1, 0xA5, 0x83,
+ },
+ }, {
+ .name = "is_data=true, len=1, c=41",
+ .is_data = true,
+ .data = { 0xff },
+ .len = 1,
+ .custom_len = 41,
+ .exp = {
+ 0xD8, 0x48, 0xC5, 0x06, 0x8C, 0xED, 0x73, 0x6F, 0x44, 0x62, 0x15, 0x9B, 0x98, 0x67, 0xFD, 0x4C,
+ 0x20, 0xB8, 0x08, 0xAC, 0xC3, 0xD5, 0xBC, 0x48, 0xE0, 0xB0, 0x6B, 0xA0, 0xA3, 0x76, 0x2E, 0xC4,
+ },
+ }, {
+ .name = "is_data=true, len=3, c=41**2",
+ .is_data = true,
+ .data = { 0xff, 0xff, 0xff },
+ .len = 3,
+ .custom_len = 41*41,
+ .exp = {
+ 0xC3, 0x89, 0xE5, 0x00, 0x9A, 0xE5, 0x71, 0x20, 0x85, 0x4C, 0x2E, 0x8C, 0x64, 0x67, 0x0A, 0xC0,
+ 0x13, 0x58, 0xCF, 0x4C, 0x1B, 0xAF, 0x89, 0x44, 0x7A, 0x72, 0x42, 0x34, 0xDC, 0x7C, 0xED, 0x74,
+ },
+ }, {
+ .name = "is_data=true, len=7, c=41**3",
+ .is_data = true,
+ .data = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
+ .len = 7,
+ .custom_len = 41*41*41,
+ .exp = {
+ 0x75, 0xD2, 0xF8, 0x6A, 0x2E, 0x64, 0x45, 0x66, 0x72, 0x6B, 0x4F, 0xBC, 0xFC, 0x56, 0x57, 0xB9,
+ 0xDB, 0xCF, 0x07, 0x0C, 0x7B, 0x0D, 0xCA, 0x06, 0x45, 0x0A, 0xB2, 0x91, 0xD7, 0x44, 0x3B, 0xCF,
+ },
+ }};
+
+ for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+ // populate source data
+ uint8_t *src = tests[i].is_data ? (uint8_t*) tests[i].data : NULL;
+ if (!tests[i].is_data && tests[i].len > 0) {
+ // alloc src
+ src = malloc(tests[i].len);
+
+ // copy src data from pattern
+ for (size_t ofs = 0; ofs < tests[i].len; ofs += sizeof(PATTERN)) {
+ const size_t len = MIN(tests[i].len - ofs, sizeof(PATTERN));
+ memcpy(src + ofs, PATTERN, len);
+ }
+ }
+
+ // populate custom string
+ uint8_t *custom = NULL;
+ if (tests[i].custom_len > 0) {
+ // alloc custom
+ custom = malloc(tests[i].custom_len);
+
+ // copy custom data from pattern
+ for (size_t ofs = 0; ofs < tests[i].custom_len; ofs += sizeof(PATTERN)) {
+ const size_t len = MIN(tests[i].custom_len - ofs, sizeof(PATTERN));
+ memcpy(custom + ofs, PATTERN, len);
+ }
+ }
+
+ // run
+ uint8_t got[32] = { 0 };
+ kangarootwelve_custom(custom, tests[i].custom_len, src, tests[i].len, got, sizeof(got));
+
+ // check
+ if (memcmp(got, tests[i].exp, sizeof(got))) {
+ fprintf(stderr, "test_kangarootwelve(\"%s\") failed, got:\n", tests[i].name);
+ dump_hex(stderr, got, sizeof(got));
+
+ fprintf(stderr, "exp:\n");
+ dump_hex(stderr, tests[i].exp, sizeof(got));
+ }
+
+ // free test data
+ if (!tests[i].is_data && tests[i].len) {
+ free(src);
+ }
+ free(custom);
+ }
+}
+
int main(void) {
test_theta();
test_rho();
@@ -6288,6 +6786,8 @@ int main(void) {
test_hmac_sha3_512_ctx();
test_turboshake128();
test_turboshake256();
+ test_kangarootwelve_length_encode();
+ test_kangarootwelve();
printf("ok\n");
}