summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Duncan <pabs@pablotron.org>2023-09-04 15:40:05 -0400
committerPaul Duncan <pabs@pablotron.org>2023-09-04 15:40:05 -0400
commit058eba2d2171e38813d6ad722333ee58668d35b1 (patch)
tree9d38fbf8a116c231a2a2f4750ea1e7f4d4679c3a
parent0e7910d8666d164517a18306089b7dfcf7433af4 (diff)
downloadsha3-058eba2d2171e38813d6ad722333ee58668d35b1.tar.bz2
sha3-058eba2d2171e38813d6ad722333ee58668d35b1.zip
sha3.[hc]: add sha3_{224,256,384,512}_{init,absorb,final}() and test_sha3_{224,256,384,512}_ctx()
-rw-r--r--sha3.c662
-rw-r--r--sha3.h127
2 files changed, 787 insertions, 2 deletions
diff --git a/sha3.c b/sha3.c
index 01dd4f8..4a7f512 100644
--- a/sha3.c
+++ b/sha3.c
@@ -213,6 +213,123 @@ void sha3_512(const uint8_t *m, size_t m_len, uint8_t dst[static 64]) {
sha3(m, m_len, dst, 64);
}
+// Initialize iterative sha3 hash context.
+static inline void sha3_init(sha3_t * const hash) {
+ memset(hash, 0, sizeof(sha3_t));
+}
+
+// Absorb input data into iterative SHA-3 hash context.
+static inline bool sha3_absorb(sha3_t * const hash, const size_t rate, const uint8_t * const src, const size_t len) {
+ if (hash->finalized) {
+ // hash already finalized, return false
+ return false;
+ }
+
+ // absorb bytes (FIXME: should absorb larger chunks)
+ for (size_t i = 0; i < len; i++) {
+ hash->a.u8[hash->num_bytes++] ^= src[i];
+ if (hash->num_bytes == rate) {
+ // permute
+ permute(hash->a.u64);
+ hash->num_bytes = 0;
+ }
+ }
+
+ // return success
+ return true;
+}
+
+// Finalize iterative SHA-3 hash context.
+static inline void sha3_final(sha3_t * const hash, const size_t rate, uint8_t * dst, const size_t dst_len) {
+ if (!hash->finalized) {
+ // mark context as final
+ hash->finalized = true;
+
+ // append suffix and padding
+ // (note: suffix and padding are ambiguous in spec)
+ hash->a.u8[hash->num_bytes] ^= 0x06;
+ hash->a.u8[rate - 1] ^= 0x80;
+
+ // permute
+ permute(hash->a.u64);
+ }
+
+ // copy to destination
+ memcpy(dst, hash->a.u8, dst_len);
+}
+
+#define SHA3_224_CAPACITY 28
+#define SHA3_224_RATE 200 - 2 * SHA3_224_CAPACITY
+
+// Initialize SHA3-224 iterative hash context.
+void sha3_224_init(sha3_t * const hash) {
+ sha3_init(hash);
+}
+
+// Absorb data into SHA3-224 iterative hash context.
+_Bool sha3_224_absorb(sha3_t * const hash, const uint8_t *src, const size_t len) {
+ return sha3_absorb(hash, SHA3_224_RATE, src, len);
+}
+
+// Finalize SHA3-224 iterative hash context.
+void sha3_224_final(sha3_t * const hash, uint8_t dst[SHA3_224_CAPACITY]) {
+ sha3_final(hash, SHA3_224_RATE, dst, SHA3_224_CAPACITY);
+}
+
+#define SHA3_256_CAPACITY 32
+#define SHA3_256_RATE 200 - 2 * SHA3_256_CAPACITY
+
+// Initialize SHA3-256 iterative hash context.
+void sha3_256_init(sha3_t * const hash) {
+ sha3_init(hash);
+}
+
+// Absorb data into SHA3-256 iterative hash context.
+_Bool sha3_256_absorb(sha3_t * const hash, const uint8_t *src, const size_t len) {
+ return sha3_absorb(hash, SHA3_256_RATE, src, len);
+}
+
+// Finalize SHA3-256 iterative hash context.
+void sha3_256_final(sha3_t * const hash, uint8_t dst[SHA3_256_CAPACITY]) {
+ sha3_final(hash, SHA3_256_RATE, dst, SHA3_256_CAPACITY);
+}
+
+#define SHA3_384_CAPACITY 48
+#define SHA3_384_RATE 200 - 2 * SHA3_384_CAPACITY
+
+// Initialize SHA3-384 iterative hash context.
+void sha3_384_init(sha3_t * const hash) {
+ sha3_init(hash);
+}
+
+// Absorb data into SHA3-384 iterative hash context.
+_Bool sha3_384_absorb(sha3_t * const hash, const uint8_t *src, const size_t len) {
+ return sha3_absorb(hash, SHA3_384_RATE, src, len);
+}
+
+// Finalize SHA3-384 iterative hash context.
+void sha3_384_final(sha3_t * const hash, uint8_t dst[SHA3_384_CAPACITY]) {
+ sha3_final(hash, SHA3_384_RATE, dst, SHA3_384_CAPACITY);
+}
+
+#define SHA3_512_CAPACITY 64
+#define SHA3_512_RATE 200 - 2 * SHA3_512_CAPACITY
+
+// Initialize SHA3-512 iterative hash context.
+void sha3_512_init(sha3_t * const hash) {
+ sha3_init(hash);
+}
+
+// Absorb data into SHA3-512 iterative hash context.
+_Bool sha3_512_absorb(sha3_t * const hash, const uint8_t *src, const size_t len) {
+ return sha3_absorb(hash, SHA3_512_RATE, src, len);
+}
+
+// Finalize SHA3-512 iterative hash context.
+void sha3_512_final(sha3_t * const hash, uint8_t dst[SHA3_512_CAPACITY]) {
+ sha3_final(hash, SHA3_512_RATE, dst, SHA3_512_CAPACITY);
+}
+
static inline void shake(const uint8_t *m, size_t m_len, uint8_t * const dst, const size_t dst_len) {
// in the sha3 xof functions, the capacity is always 2 times the
// destination length, and the rate is the total state size minus the
@@ -2005,6 +2122,547 @@ static void test_sha3_512(void) {
}
}
+static void test_sha3_224_ctx(void) {
+ static const struct {
+ const char *name; // test name
+ const uint8_t msg[256]; // test message
+ const size_t len; // test message length
+ const uint8_t exp[28]; // expected hash
+ } tests[] = {{
+ .name = "empty",
+ .msg = "",
+ .len = 0,
+ .exp = {
+ 0x6b, 0x4e, 0x03, 0x42, 0x36, 0x67, 0xdb, 0xb7,
+ 0x3b, 0x6e, 0x15, 0x45, 0x4f, 0x0e, 0xb1, 0xab,
+ 0xd4, 0x59, 0x7f, 0x9a, 0x1b, 0x07, 0x8e, 0x3f,
+ 0x5b, 0x5a, 0x6b, 0xc7,
+ },
+ }, {
+ .name = "asdf",
+ .msg = "asdf",
+ .len = 4,
+ .exp = {
+ 0x47, 0xcc, 0xa2, 0x03, 0xee, 0xfc, 0xa3, 0x49,
+ 0xde, 0xaa, 0x41, 0x8d, 0xa7, 0xfe, 0x05, 0x38,
+ 0x9e, 0xf6, 0x49, 0x98, 0x5c, 0x87, 0xa9, 0xb6,
+ 0x55, 0xad, 0x12, 0x8c,
+ },
+ }, {
+ .name = "a-134",
+ .msg = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ .len = 134,
+ .exp = {
+ 0x5d, 0x1d, 0xb4, 0xe4, 0x7c, 0x0c, 0x4a, 0x42,
+ 0x45, 0xb9, 0x0d, 0x18, 0x55, 0xb0, 0x7c, 0xb1,
+ 0x6f, 0xb9, 0x40, 0xc5, 0x00, 0x76, 0xc1, 0xfd,
+ 0x2a, 0xc2, 0x17, 0xfe,
+ },
+ }, {
+ .name = "a-135",
+ .msg = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ .len = 135,
+ .exp = {
+ 0xf9, 0xf2, 0x8c, 0x21, 0xa2, 0xb0, 0x88, 0x4b,
+ 0xbd, 0x35, 0x94, 0xca, 0xe8, 0x2b, 0xf8, 0x11,
+ 0xc0, 0xc1, 0xed, 0xe4, 0x27, 0xe0, 0x83, 0xd5,
+ 0x57, 0x6e, 0x90, 0x9d,
+ },
+ }, {
+ .name = "a-136",
+ .msg = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ .len = 136,
+ .exp = {
+ 0x96, 0x13, 0x6a, 0x6a, 0x09, 0x44, 0x33, 0xb4,
+ 0xaa, 0x85, 0x5f, 0x16, 0x38, 0x29, 0xa2, 0xce,
+ 0x6b, 0xca, 0x7d, 0x56, 0xcf, 0xd2, 0x16, 0x3b,
+ 0x47, 0xf1, 0xf1, 0xc4,
+ },
+ }, {
+ .name = "ff-256",
+ .msg = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ .len = 256,
+ .exp = {
+ 0xb6, 0x53, 0x4d, 0x8b, 0xee, 0xdf, 0xb5, 0xed,
+ 0x3f, 0x95, 0x3b, 0x09, 0xd1, 0x2f, 0xc3, 0x8f,
+ 0x3d, 0x8b, 0x0b, 0xea, 0x9d, 0x80, 0xbd, 0x1e,
+ 0x25, 0xc9, 0xfc, 0x35,
+ },
+ }, {
+ .name = "a-210",
+ .msg = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ .len = 210,
+ .exp = {
+ 0x03, 0x05, 0x4a, 0xb0, 0xa3, 0x37, 0x99, 0xbe,
+ 0x2b, 0x17, 0xc8, 0x2b, 0x3a, 0x13, 0x90, 0x05,
+ 0x2e, 0x49, 0xdf, 0x31, 0x6d, 0x85, 0x15, 0x2d,
+ 0x74, 0x66, 0xab, 0x57,
+ },
+ }};
+
+ // run all tests
+ for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+ // try all possible absorb sizes from 1 to len
+ for (size_t j = 1; j < tests[i].len; j++) {
+ // init
+ sha3_t hash;
+ sha3_224_init(&hash);
+
+ // absorb
+ for (size_t k = 0; k < tests[i].len; k += j) {
+ const size_t absorb_len = MIN(tests[i].len - k, j);
+ sha3_224_absorb(&hash, tests[i].msg + k, absorb_len);
+ }
+
+ // finalize
+ uint8_t got[28] = { 0 };
+ sha3_224_final(&hash, got);
+
+ // check
+ if (memcmp(got, tests[i].exp, sizeof(got))) {
+ fprintf(stderr, "test_sha3_224_ctx(\"%s\") failed, got:\n", tests[i].name);
+ dump_hex(stderr, got, 28);
+
+ fprintf(stderr, "exp:\n");
+ dump_hex(stderr, tests[i].exp, 28);
+ }
+ }
+ }
+}
+
+static void test_sha3_256_ctx(void) {
+ static const struct {
+ const char *name; // test name
+ const uint8_t msg[256]; // test message
+ const size_t len; // test message length
+ const uint8_t exp[32]; // expected hash
+ } tests[] = {{
+ .name = "empty",
+ .msg = "",
+ .len = 0,
+ .exp = {
+ 0xa7, 0xff, 0xc6, 0xf8, 0xbf, 0x1e, 0xd7, 0x66,
+ 0x51, 0xc1, 0x47, 0x56, 0xa0, 0x61, 0xd6, 0x62,
+ 0xf5, 0x80, 0xff, 0x4d, 0xe4, 0x3b, 0x49, 0xfa,
+ 0x82, 0xd8, 0x0a, 0x4b, 0x80, 0xf8, 0x43, 0x4a,
+ },
+ }, {
+ .name = "asdf",
+ .msg = "asdf",
+ .len = 4,
+ .exp = {
+ 0xdd, 0x27, 0x81, 0xf4, 0xc5, 0x1b, 0xcc, 0xdb,
+ 0xe2, 0x3e, 0x4d, 0x39, 0x8b, 0x8a, 0x82, 0x26,
+ 0x1f, 0x58, 0x5c, 0x27, 0x8d, 0xbb, 0x4b, 0x84,
+ 0x98, 0x9f, 0xea, 0x70, 0xe7, 0x67, 0x23, 0xa9,
+ },
+ }, {
+ .name = "a-134",
+ .msg = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ .len = 134,
+ .exp = {
+ 0x58, 0xb9, 0x70, 0xc3, 0x7a, 0xc2, 0xd6, 0x5b,
+ 0x59, 0x9b, 0x69, 0x18, 0x68, 0xa6, 0x14, 0x01,
+ 0xa5, 0x01, 0xc4, 0x0f, 0x23, 0x5d, 0x55, 0xf0,
+ 0x59, 0xd3, 0x9a, 0x94, 0x2f, 0x41, 0xdc, 0xee,
+ },
+ }, {
+ .name = "a-135",
+ .msg = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ .len = 135,
+ .exp = {
+ 0x80, 0x94, 0xbb, 0x53, 0xc4, 0x4c, 0xfb, 0x1e,
+ 0x67, 0xb7, 0xc3, 0x04, 0x47, 0xf9, 0xa1, 0xc3,
+ 0x36, 0x96, 0xd2, 0x46, 0x3e, 0xcc, 0x1d, 0x9c,
+ 0x92, 0x53, 0x89, 0x13, 0x39, 0x28, 0x43, 0xc9,
+ },
+ }, {
+ .name = "a-136",
+ .msg = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ .len = 136,
+ .exp = {
+ 0x3f, 0xc5, 0x55, 0x9f, 0x14, 0xdb, 0x8e, 0x45,
+ 0x3a, 0x0a, 0x30, 0x91, 0xed, 0xbd, 0x2b, 0xc2,
+ 0x5e, 0x11, 0x52, 0x8d, 0x81, 0xc6, 0x6f, 0xa5,
+ 0x70, 0xa4, 0xef, 0xdc, 0xc2, 0x69, 0x5e, 0xe1,
+ },
+ }, {
+ .name = "ff-256",
+ .msg = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ .len = 256,
+ .exp = {
+ 0xea, 0xa5, 0x7c, 0x69, 0x9a, 0x7a, 0x61, 0x4f,
+ 0x9d, 0x96, 0x1e, 0x42, 0xd8, 0xb1, 0x2a, 0x93,
+ 0x54, 0x6e, 0x8e, 0x80, 0xd3, 0x1f, 0x5c, 0xfc,
+ 0xc4, 0x3f, 0x95, 0x39, 0xed, 0x06, 0x30, 0x7a,
+ },
+ }, {
+ .name = "a-210",
+ .msg = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ .len = 210,
+ .exp = {
+ 0xb4, 0x12, 0xbd, 0x70, 0xce, 0x67, 0xeb, 0x59,
+ 0x0b, 0xb2, 0x25, 0x09, 0xef, 0x4e, 0x68, 0x7e,
+ 0x02, 0xb4, 0x8e, 0x07, 0xd2, 0xbb, 0xd7, 0xbb,
+ 0x9a, 0xfb, 0x58, 0x70, 0xfd, 0x14, 0xc1, 0x13,
+ },
+ }};
+
+ // run all tests
+ for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+ // try all possible absorb sizes from 1 to len
+ for (size_t j = 1; j < tests[i].len; j++) {
+ // init context
+ sha3_t hash;
+ sha3_256_init(&hash);
+
+ // absorb
+ for (size_t k = 0; k < tests[i].len; k += j) {
+ const size_t absorb_len = MIN(tests[i].len - k, j);
+ sha3_256_absorb(&hash, tests[i].msg + k, absorb_len);
+ }
+
+ // finalize
+ uint8_t got[32] = { 0 };
+ sha3_256_final(&hash, got);
+
+ // check
+ if (memcmp(got, tests[i].exp, sizeof(got))) {
+ fprintf(stderr, "test_sha3_256_ctx(\"%s\") failed, got:\n", tests[i].name);
+ dump_hex(stderr, got, 32);
+
+ fprintf(stderr, "exp:\n");
+ dump_hex(stderr, tests[i].exp, 32);
+ }
+ }
+ }
+}
+
+static void test_sha3_384_ctx(void) {
+ static const struct {
+ const char *name; // test name
+ const uint8_t msg[256]; // test message
+ const size_t len; // test message length
+ const uint8_t exp[48]; // expected hash
+ } tests[] = {{
+ .name = "empty",
+ .msg = "",
+ .len = 0,
+ .exp = {
+ 0x0c, 0x63, 0xa7, 0x5b, 0x84, 0x5e, 0x4f, 0x7d,
+ 0x01, 0x10, 0x7d, 0x85, 0x2e, 0x4c, 0x24, 0x85,
+ 0xc5, 0x1a, 0x50, 0xaa, 0xaa, 0x94, 0xfc, 0x61,
+ 0x99, 0x5e, 0x71, 0xbb, 0xee, 0x98, 0x3a, 0x2a,
+ 0xc3, 0x71, 0x38, 0x31, 0x26, 0x4a, 0xdb, 0x47,
+ 0xfb, 0x6b, 0xd1, 0xe0, 0x58, 0xd5, 0xf0, 0x04,
+ },
+ }, {
+ .name = "asdf",
+ .msg = "asdf",
+ .len = 4,
+ .exp = {
+ 0xcc, 0x8c, 0x24, 0x61, 0xa8, 0xac, 0xb5, 0x8c,
+ 0x9e, 0x7b, 0x78, 0x95, 0x8e, 0xa3, 0x6d, 0x5f,
+ 0xd3, 0x20, 0x38, 0xf9, 0x58, 0xcd, 0xbb, 0x59,
+ 0xf2, 0x6b, 0x0f, 0xfd, 0x1c, 0x36, 0x57, 0x61,
+ 0x83, 0xe1, 0xc2, 0xca, 0x33, 0x57, 0xca, 0x1e,
+ 0x33, 0x3a, 0x11, 0xaf, 0xac, 0x8a, 0xed, 0xe6,
+ },
+ }, {
+ .name = "a-134",
+ .msg = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ .len = 134,
+ .exp = {
+ 0xac, 0x8b, 0xca, 0x5d, 0x14, 0xed, 0xb5, 0xfc,
+ 0x82, 0xe2, 0x2e, 0x45, 0x33, 0x34, 0xcb, 0x39,
+ 0xef, 0x43, 0x14, 0xa5, 0x9a, 0xd7, 0xba, 0x6f,
+ 0xb1, 0x0f, 0x0b, 0x11, 0xbe, 0x7b, 0x4a, 0xf3,
+ 0xfd, 0xe3, 0xe9, 0x54, 0x07, 0x81, 0xc2, 0x39,
+ 0xfa, 0x4c, 0x1f, 0x60, 0x44, 0xf3, 0x1d, 0xa9,
+ },
+ }, {
+ .name = "a-135",
+ .msg = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ .len = 135,
+ .exp = {
+ 0xa2, 0xd5, 0x19, 0x07, 0xc0, 0x61, 0x1e, 0x25,
+ 0xc0, 0x58, 0xf0, 0x67, 0x50, 0x42, 0xe8, 0xf5,
+ 0x3c, 0xc4, 0x73, 0xdc, 0x34, 0x7c, 0x5e, 0xa8,
+ 0xa8, 0x13, 0xd8, 0x86, 0xb3, 0xaa, 0x8f, 0x8d,
+ 0xca, 0xb6, 0x1a, 0x23, 0x62, 0x37, 0xd9, 0x4d,
+ 0xe4, 0x04, 0xcd, 0x66, 0x60, 0x62, 0x43, 0xf9,
+ },
+ }, {
+ .name = "a-136",
+ .msg = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ .len = 136,
+ .exp = {
+ 0xcb, 0xbc, 0xb4, 0x66, 0x41, 0x7a, 0x2f, 0x6d,
+ 0x46, 0x64, 0x79, 0xbb, 0x6d, 0xc6, 0x59, 0x43,
+ 0x4d, 0x95, 0x89, 0xde, 0x3a, 0x53, 0xac, 0xc9,
+ 0xb4, 0x27, 0x58, 0x04, 0x82, 0xe3, 0x05, 0x94,
+ 0x88, 0x88, 0xc8, 0xfa, 0x6d, 0x06, 0x9c, 0x5e,
+ 0x6a, 0x89, 0x9a, 0xa3, 0x4a, 0x9a, 0xf1, 0x5a,
+ },
+ }, {
+ .name = "ff-256",
+ .msg = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ .len = 256,
+ .exp = {
+ 0x91, 0xb2, 0xc0, 0xab, 0x2c, 0xe3, 0x31, 0xcd,
+ 0x59, 0xab, 0x98, 0x3b, 0x87, 0xaf, 0xc9, 0xe5,
+ 0x45, 0x66, 0x33, 0xb1, 0x30, 0x49, 0xac, 0x84,
+ 0x36, 0x04, 0xc9, 0xb6, 0xc3, 0xab, 0x09, 0xc3,
+ 0x5c, 0xf8, 0x8c, 0xcb, 0xf7, 0x61, 0xc5, 0x64,
+ 0x7c, 0x92, 0x2b, 0xcc, 0x9b, 0x37, 0x5a, 0x6b,
+ },
+ }, {
+ .name = "a-210",
+ .msg = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ .len = 210,
+ .exp = {
+ 0x23, 0x2f, 0xba, 0x5c, 0x81, 0x59, 0x36, 0xc7,
+ 0x7b, 0x37, 0x3a, 0x5e, 0x5d, 0xa0, 0xd2, 0xca,
+ 0x3b, 0x15, 0xcd, 0x28, 0x58, 0x92, 0x66, 0x14,
+ 0x5a, 0x92, 0xd6, 0x50, 0xfa, 0x58, 0xef, 0x4f,
+ 0xdf, 0xa3, 0x4e, 0x9e, 0x2f, 0x0e, 0xa8, 0x7a,
+ 0x67, 0x32, 0x8f, 0x68, 0x9a, 0x6b, 0x8f, 0x04,
+ },
+ }};
+
+ // run all tests
+ for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+ // try all possible absorb sizes from 1 to len
+ for (size_t j = 1; j < tests[i].len; j++) {
+ // init
+ sha3_t hash;
+ sha3_384_init(&hash);
+
+ // absorb
+ for (size_t k = 0; k < tests[i].len; k += j) {
+ const size_t absorb_len = MIN(tests[i].len - k, j);
+ sha3_384_absorb(&hash, tests[i].msg + k, absorb_len);
+ }
+
+ // finalize
+ uint8_t got[48] = { 0 };
+ sha3_384_final(&hash, got);
+
+ // check
+ if (memcmp(got, tests[i].exp, sizeof(got))) {
+ fprintf(stderr, "test_sha3_384_ctx(\"%s\") failed, got:\n", tests[i].name);
+ dump_hex(stderr, got, 48);
+
+ fprintf(stderr, "exp:\n");
+ dump_hex(stderr, tests[i].exp, 48);
+ }
+ }
+ }
+}
+
+static void test_sha3_512_ctx(void) {
+ static const struct {
+ const char *name; // test name
+ const uint8_t msg[256]; // test message
+ const size_t len; // test message length
+ const uint8_t exp[64]; // expected hash
+ } tests[] = {{
+ .name = "empty",
+ .msg = "",
+ .len = 0,
+ .exp = {
+ 0xa6, 0x9f, 0x73, 0xcc, 0xa2, 0x3a, 0x9a, 0xc5,
+ 0xc8, 0xb5, 0x67, 0xdc, 0x18, 0x5a, 0x75, 0x6e,
+ 0x97, 0xc9, 0x82, 0x16, 0x4f, 0xe2, 0x58, 0x59,
+ 0xe0, 0xd1, 0xdc, 0xc1, 0x47, 0x5c, 0x80, 0xa6,
+ 0x15, 0xb2, 0x12, 0x3a, 0xf1, 0xf5, 0xf9, 0x4c,
+ 0x11, 0xe3, 0xe9, 0x40, 0x2c, 0x3a, 0xc5, 0x58,
+ 0xf5, 0x00, 0x19, 0x9d, 0x95, 0xb6, 0xd3, 0xe3,
+ 0x01, 0x75, 0x85, 0x86, 0x28, 0x1d, 0xcd, 0x26,
+ },
+ }, {
+ .name = "asdf",
+ .msg = "asdf",
+ .len = 4,
+ .exp = {
+ 0x8d, 0x88, 0xcf, 0x5b, 0x20, 0xf5, 0x3a, 0xcd,
+ 0x7a, 0xe1, 0x47, 0x9b, 0x5b, 0x36, 0xdc, 0x20,
+ 0x21, 0x75, 0x3b, 0x04, 0x99, 0x02, 0xc7, 0x72,
+ 0x47, 0xbb, 0x27, 0xb1, 0x31, 0xb3, 0x00, 0xbd,
+ 0x3c, 0xa8, 0xbe, 0xef, 0x28, 0x75, 0x6d, 0xce,
+ 0x27, 0xb8, 0x99, 0x08, 0x67, 0xc4, 0x57, 0x7a,
+ 0x25, 0x35, 0xe7, 0xe3, 0xb7, 0x51, 0x41, 0x39,
+ 0x9c, 0xa1, 0xa9, 0x4c, 0xc8, 0x4b, 0x0e, 0xb9,
+ },
+ }, {
+ .name = "a-134",
+ .msg = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ .len = 134,
+ .exp = {
+ 0x4e, 0x72, 0xa4, 0xf6, 0x55, 0x99, 0xa2, 0x47,
+ 0x2a, 0x66, 0xf8, 0xa8, 0xd6, 0xcf, 0xb3, 0x84,
+ 0xb7, 0xd3, 0x4f, 0x52, 0xec, 0xaa, 0x00, 0xe9,
+ 0xf0, 0x66, 0x82, 0x0b, 0x0e, 0xdc, 0x1f, 0xcc,
+ 0x9c, 0xf3, 0x12, 0x0b, 0x81, 0x46, 0x89, 0xbc,
+ 0x62, 0xb7, 0x72, 0x0f, 0x4e, 0x4c, 0x99, 0x3d,
+ 0xa0, 0x84, 0x08, 0x27, 0xf6, 0x95, 0x03, 0x8a,
+ 0x82, 0x18, 0x69, 0xe8, 0xa6, 0x27, 0x60, 0xe8,
+ },
+ }, {
+ .name = "a-135",
+ .msg = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ .len = 135,
+ .exp = {
+ 0x4b, 0xe1, 0xe7, 0x02, 0x76, 0xf9, 0x12, 0x2f,
+ 0x47, 0x0a, 0x54, 0xc2, 0x72, 0x40, 0xc7, 0xd0,
+ 0x70, 0x9d, 0xab, 0x74, 0x69, 0x95, 0x8b, 0x48,
+ 0xa9, 0x50, 0xd6, 0x9d, 0xa6, 0xdd, 0x07, 0xca,
+ 0x13, 0x58, 0x26, 0xd9, 0xd2, 0x3e, 0x97, 0x5c,
+ 0xb9, 0x28, 0x3e, 0x7d, 0x23, 0x6e, 0xf9, 0x8a,
+ 0x80, 0x45, 0x1d, 0xca, 0x8e, 0x31, 0x1f, 0x52,
+ 0x09, 0x63, 0x08, 0xb2, 0xc8, 0xd7, 0x0c, 0xc7,
+ },
+ }, {
+ .name = "a-136",
+ .msg = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ .len = 136,
+ .exp = {
+ 0xe5, 0x03, 0x92, 0xc9, 0x1e, 0xd9, 0x57, 0x68,
+ 0xc8, 0xdc, 0xf5, 0x2a, 0x12, 0xe5, 0xdb, 0x1e,
+ 0xcd, 0x03, 0x47, 0xfb, 0x99, 0x5f, 0x7f, 0xf4,
+ 0xea, 0x06, 0x99, 0x46, 0x49, 0xbb, 0xd1, 0xa0,
+ 0xde, 0x7a, 0xe3, 0x6a, 0x62, 0xaa, 0xdc, 0x00,
+ 0xa7, 0x04, 0xd7, 0x30, 0xb5, 0x2b, 0xda, 0x19,
+ 0x1b, 0x72, 0x95, 0x1e, 0x2a, 0xfc, 0x9b, 0x6f,
+ 0xb6, 0x82, 0x47, 0x87, 0xb2, 0x08, 0x62, 0x57,
+ },
+ }, {
+ .name = "ff-256",
+ .msg = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ .len = 256,
+ .exp = {
+ 0x02, 0x3a, 0x91, 0x27, 0xcf, 0x29, 0x6d, 0x01,
+ 0x12, 0x0e, 0x3d, 0xaa, 0x92, 0x76, 0x15, 0x23,
+ 0x8e, 0x97, 0xdd, 0xb6, 0x33, 0x26, 0xe2, 0x89,
+ 0xb3, 0x31, 0xe2, 0x25, 0xbe, 0x41, 0xea, 0xd5,
+ 0x90, 0xf1, 0xdd, 0x5c, 0x80, 0x1d, 0x91, 0x03,
+ 0xfe, 0x65, 0x7d, 0x23, 0x63, 0xbd, 0xb2, 0x66,
+ 0xe7, 0x90, 0xb1, 0x89, 0x0b, 0x3d, 0xd5, 0x79,
+ 0xdc, 0xa7, 0xf9, 0x1f, 0x5f, 0x4d, 0x98, 0x49,
+ },
+ }, {
+ .name = "a-210",
+ .msg = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ .len = 210,
+ .exp = {
+ 0x47, 0xfa, 0xd7, 0x8d, 0x72, 0x35, 0xe5, 0xcd,
+ 0xb9, 0xd7, 0x23, 0x6c, 0xfc, 0xf2, 0x79, 0xdb,
+ 0x0f, 0x8a, 0xcd, 0x5b, 0xf1, 0x7b, 0xdc, 0x74,
+ 0x18, 0xe4, 0x29, 0xb7, 0x1a, 0x0a, 0x21, 0xb7,
+ 0xed, 0xe8, 0x54, 0x64, 0xcf, 0x69, 0x44, 0x63,
+ 0xff, 0x0e, 0x56, 0xf2, 0x8f, 0x04, 0xbf, 0x85,
+ 0xc0, 0x85, 0xe1, 0x17, 0x6a, 0x47, 0xd7, 0x3b,
+ 0xf2, 0x8b, 0xdb, 0xa0, 0x4f, 0x4a, 0xe7, 0x61,
+ },
+ }};
+
+ for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+ // try all possible absorb sizes from 1 to len
+ for (size_t j = 1; j < tests[i].len; j++) {
+ // init
+ sha3_t hash;
+ sha3_512_init(&hash);
+
+ // absorb
+ for (size_t k = 0; k < tests[i].len; k += j) {
+ const size_t absorb_len = MIN(tests[i].len - k, j);
+ sha3_512_absorb(&hash, tests[i].msg + k, absorb_len);
+ }
+
+ // finalize
+ uint8_t got[64] = { 0 };
+ sha3_512_final(&hash, got);
+
+ // check
+ if (memcmp(got, tests[i].exp, sizeof(got))) {
+ fprintf(stderr, "test_sha3_512_ctx(\"%s\") failed, got:\n", tests[i].name);
+ dump_hex(stderr, got, 64);
+
+ fprintf(stderr, "exp:\n");
+ dump_hex(stderr, tests[i].exp, 64);
+ }
+ }
+ }
+}
+
static void test_shake128(void) {
static const struct {
const char *name; // test name
@@ -4238,6 +4896,10 @@ int main(void) {
test_sha3_256();
test_sha3_384();
test_sha3_512();
+ test_sha3_224_ctx();
+ test_sha3_256_ctx();
+ test_sha3_384_ctx();
+ test_sha3_512_ctx();
test_shake128();
test_shake256();
test_shake128_xof();
diff --git a/sha3.h b/sha3.h
index 9e2a015..003d5b5 100644
--- a/sha3.h
+++ b/sha3.h
@@ -11,13 +11,20 @@ extern "C" {
#include <stdint.h>
-// sha3 state
+// Internal sha3 state.
typedef union {
uint8_t u8[200];
uint64_t u64[25];
} sha3_state_t;
-// Internal XOF state
+// Iterative SHA-3 context (all members are private).
+typedef struct {
+ size_t num_bytes; // number of bytes absorbed
+ sha3_state_t a; // internal state
+ _Bool finalized; // mode (absorbing or finalized)
+} sha3_t;
+
+// Iterative XOF context (all members are private).
typedef struct {
size_t num_bytes; // number of bytes absorbed
sha3_state_t a; // internal state
@@ -36,6 +43,35 @@ typedef struct {
void sha3_224(const uint8_t *m, size_t m_len, uint8_t dst[static 28]);
/**
+ * Initialize SHA3-224 hash context.
+ *
+ * @param[out] hash SHA3-224 hash context.
+ */
+void sha3_224_init(sha3_t *hash);
+
+/**
+ * Absorb input data in `src` of length `len` bytes into SHA3-224 hash
+ * context `hash`. Can be called iteratively to absorb input data in
+ * chunks.
+ *
+ * @param[in/out] hash SHA3-224 hash context.
+ * @param[in] src Input data.
+ * @param[in] len Input data length, in bytes.
+ *
+ * @return True if data was absorbed, and false otherwise (e.g., if context has already been finalized).
+ */
+_Bool sha3_224_absorb(sha3_t *hash, const uint8_t *src, const size_t len);
+
+/**
+ * Finalize SHA3-224 hash context and write 28 bytes of output to
+ * destination buffer `dst`.
+ *
+ * @param[in/out] hash SHA3-224 hash context.
+ * @param[out] dst Destination buffer. Must be at least 28 bytes in length.
+ */
+void sha3_224_final(sha3_t *hash, uint8_t dst[28]);
+
+/**
* Hash input message in input buffer `m` of length `m_len` bytes with
* SHA3-256 (FIPS 202, section 6.1) and write 32 bytes of output to
* destination buffer `dst`.
@@ -47,6 +83,35 @@ void sha3_224(const uint8_t *m, size_t m_len, uint8_t dst[static 28]);
void sha3_256(const uint8_t *m, size_t m_len, uint8_t dst[static 32]);
/**
+ * Initialize SHA3-256 hash context.
+ *
+ * @param[out] hash SHA3-256 hash context.
+ */
+void sha3_256_init(sha3_t *hash);
+
+/**
+ * Absorb input data in `src` of length `len` bytes into SHA3-256 hash
+ * context `hash`. Can be called iteratively to absorb input data in
+ * chunks.
+ *
+ * @param[in/out] hash SHA3-256 hash context.
+ * @param[in] src Input data.
+ * @param[in] len Input data length, in bytes.
+ *
+ * @return True if data was absorbed, and false otherwise (e.g., if context has already been finalized).
+ */
+_Bool sha3_256_absorb(sha3_t *hash, const uint8_t *src, const size_t len);
+
+/**
+ * Finalize SHA3-256 hash context and write 28 bytes of output to
+ * destination buffer `dst`.
+ *
+ * @param[in/out] hash SHA3-256 hash context.
+ * @param[out] dst Destination buffer. Must be at least 32 bytes in length.
+ */
+void sha3_256_final(sha3_t *hash, uint8_t dst[32]);
+
+/**
* Hash input message in input buffer `m` of length `m_len` bytes with
* SHA3-384 (FIPS 202, section 6.1) and write 48 bytes of output to
* destination buffer `dst`.
@@ -58,6 +123,35 @@ void sha3_256(const uint8_t *m, size_t m_len, uint8_t dst[static 32]);
void sha3_384(const uint8_t *m, size_t m_len, uint8_t dst[static 48]);
/**
+ * Initialize SHA3-384 hash context.
+ *
+ * @param[out] hash SHA3-384 hash context.
+ */
+void sha3_384_init(sha3_t *hash);
+
+/**
+ * Absorb input data in `src` of length `len` bytes into SHA3-384 hash
+ * context `hash`. Can be called iteratively to absorb input data in
+ * chunks.
+ *
+ * @param[in/out] hash SHA3-384 hash context.
+ * @param[in] src Input data.
+ * @param[in] len Input data length, in bytes.
+ *
+ * @return True if data was absorbed, and false otherwise (e.g., if context has already been finalized).
+ */
+_Bool sha3_384_absorb(sha3_t *hash, const uint8_t *src, const size_t len);
+
+/**
+ * Finalize SHA3-384 hash context and write 28 bytes of output to
+ * destination buffer `dst`.
+ *
+ * @param[in/out] hash SHA3-384 hash context.
+ * @param[out] dst Destination buffer. Must be at least 48 bytes in length.
+ */
+void sha3_384_final(sha3_t *hash, uint8_t dst[48]);
+
+/**
* Hash input message in input buffer `m` of length `m_len` bytes with
* SHA3-512 (FIPS 202, section 6.1) and write 64 bytes of output to
* destination buffer `dst`.
@@ -69,6 +163,35 @@ void sha3_384(const uint8_t *m, size_t m_len, uint8_t dst[static 48]);
void sha3_512(const uint8_t *m, size_t m_len, uint8_t dst[static 64]);
/**
+ * Initialize SHA3-512 hash context.
+ *
+ * @param[out] hash SHA3-512 hash context.
+ */
+void sha3_512_init(sha3_t *hash);
+
+/**
+ * Absorb input data in `src` of length `len` bytes into SHA3-512 hash
+ * context `hash`. Can be called iteratively to absorb input data in
+ * chunks.
+ *
+ * @param[in/out] hash SHA3-512 hash context.
+ * @param[in] src Input data.
+ * @param[in] len Input data length, in bytes.
+ *
+ * @return True if data was absorbed, and false otherwise (e.g., if context has already been finalized).
+ */
+_Bool sha3_512_absorb(sha3_t *hash, const uint8_t *src, const size_t len);
+
+/**
+ * Finalize SHA3-512 hash context and write 28 bytes of output to
+ * destination buffer `dst`.
+ *
+ * @param[in/out] hash SHA3-512 hash context.
+ * @param[out] dst Destination buffer. Must be at least 64 bytes in length.
+ */
+void sha3_512_final(sha3_t *hash, uint8_t dst[64]);
+
+/**
* Hash input message in buffer `m` of length `m_len` bytes with
* SHAKE128 (FIPS 202, section 6.2) and write 16 bytes of output to
* destination buffer `dst`.