Welcome to little lamb

Code » test-hashes » commit 3a4cd5a

First commit

author Olivier Brunel
2023-01-25 10:24:25 UTC
committer Olivier Brunel
2023-01-25 12:19:00 UTC
parent f4ea198d1cb1a80c73b38eab1343c2eec5687c19

First commit

.gitignore +4 -0
include/sha3-impl.h +74 -0
meta/AUTHORS +3 -0
meta/deps-bin +6 -0
meta/deps/limb/configure +0 -0
meta/deps/limb/files +0 -0
meta/deps/limb/git +1 -0
meta/deps/limb/static +0 -0
meta/deps/limb/version +1 -0
meta/deps/skalibs/get_version +2 -0
meta/deps/skalibs/git +1 -0
meta/deps/skalibs/incdir +1 -0
meta/deps/skalibs/include +0 -0
meta/deps/skalibs/libdir +1 -0
meta/deps/skalibs/library +1 -0
meta/deps/skalibs/version +1 -0
project.mk +3 -6
src/blake2s-ska.c +23 -0
src/blake3-lila.c +23 -0
src/sha1-ska.c +23 -0
src/sha256-ska.c +23 -0
src/sha3-impl.c +325 -0
src/sha3-lila.c +23 -0
src/sha3.c +24 -0
src/test.c +58 -0
tests +11 -0

diff --git a/.gitignore b/.gitignore
index 7ccd516..8708773 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,7 @@
 *.o
 *.lo
 *.d
+/skalibs
+/limb
+/limb.built
+/test-*
diff --git a/include/sha3-impl.h b/include/sha3-impl.h
new file mode 100644
index 0000000..8d9c8aa
--- /dev/null
+++ b/include/sha3-impl.h
@@ -0,0 +1,74 @@
+#ifndef SHA3_H
+#define SHA3_H
+
+#include <string.h> /* size_t */
+#include <stdint.h>
+
+/* -------------------------------------------------------------------------
+ * Works when compiled for either 32-bit or 64-bit targets, optimized for 
+ * 64 bit.
+ *
+ * Canonical implementation of Init/Update/Finalize for SHA-3 byte input. 
+ *
+ * SHA3-256, SHA3-384, SHA-512 are implemented. SHA-224 can easily be added.
+ *
+ * Based on code from http://keccak.noekeon.org/ .
+ *
+ * I place the code that I wrote into public domain, free to use. 
+ *
+ * I would appreciate if you give credits to this work if you used it to 
+ * write or test * your code.
+ *
+ * Aug 2015. Andrey Jivsov. crypto@brainhub.org
+ * ---------------------------------------------------------------------- */
+
+/* 'Words' here refers to uint64_t */
+#define SHA3_KECCAK_SPONGE_WORDS \
+	(((1600)/8/*bits to byte*/)/sizeof(uint64_t))
+typedef struct sha3_context_ {
+    uint64_t saved;             /* the portion of the input message that we
+                                 * didn't consume yet */
+    union {                     /* Keccak's state */
+        uint64_t s[SHA3_KECCAK_SPONGE_WORDS];
+        uint8_t sb[SHA3_KECCAK_SPONGE_WORDS * 8];
+    } u;
+    unsigned byteIndex;         /* 0..7--the next byte after the set one
+                                 * (starts from 0; 0--none are buffered) */
+    unsigned wordIndex;         /* 0..24--the next word to integrate input
+                                 * (starts from 0) */
+    unsigned capacityWords;     /* the double size of the hash output in
+                                 * words (e.g. 16 for Keccak 512) */
+} sha3_context;
+
+enum SHA3_FLAGS {
+    SHA3_FLAGS_NONE=0,
+    SHA3_FLAGS_KECCAK=1
+};
+
+enum SHA3_RETURN {
+    SHA3_RETURN_OK=0,
+    SHA3_RETURN_BAD_PARAMS=1
+};
+typedef enum SHA3_RETURN sha3_return_t;
+
+/* For Init or Reset call these: */
+sha3_return_t sha3_Init(void *priv, unsigned bitSize);
+
+void sha3_Init256(void *priv);
+void sha3_Init384(void *priv);
+void sha3_Init512(void *priv);
+
+enum SHA3_FLAGS sha3_SetFlags(void *priv, enum SHA3_FLAGS);
+
+void sha3_Update(void *priv, void const *bufIn, size_t len);
+
+void const *sha3_Finalize(void *priv);
+
+/* Single-call hashing */
+sha3_return_t sha3_HashBuffer( 
+    unsigned bitSize,   /* 256, 384, 512 */
+    enum SHA3_FLAGS flags, /* SHA3_FLAGS_NONE or SHA3_FLAGS_KECCAK */
+    const void *in, unsigned inBytes, 
+    void *out, unsigned outBytes );     /* up to bitSize/8; truncation OK */
+
+#endif
diff --git a/meta/AUTHORS b/meta/AUTHORS
index 6aef6f1..ea28dfb 100644
--- a/meta/AUTHORS
+++ b/meta/AUTHORS
@@ -1,2 +1,5 @@
 Main author:
 * Olivier Brunel <jjk@jjacky.com>
+
+Contributors:
+* Andrey Jivsov. crypto@brainhub.org [sha3-impl]
diff --git a/meta/deps-bin b/meta/deps-bin
new file mode 100644
index 0000000..0b1c3ec
--- /dev/null
+++ b/meta/deps-bin
@@ -0,0 +1,6 @@
+test-sha1-ska: src/test.o src/sha1-ska.o skalibs
+test-sha256-ska: src/test.o src/sha256-ska.o skalibs
+test-blake2s-ska: src/test.o src/blake2s-ska.o skalibs
+test-sha3-lila: src/test.o src/sha3-lila.o limb skalibs
+test-blake3-lila: src/test.o src/blake3-lila.o limb skalibs
+test-sha3: src/test.o skalibs src/sha3-impl.o src/sha3.o
diff --git a/meta/deps/limb/configure b/meta/deps/limb/configure
new file mode 100644
index 0000000..e69de29
diff --git a/meta/deps/limb/files b/meta/deps/limb/files
new file mode 100644
index 0000000..e69de29
diff --git a/meta/deps/limb/git b/meta/deps/limb/git
new file mode 100644
index 0000000..109fd23
--- /dev/null
+++ b/meta/deps/limb/git
@@ -0,0 +1 @@
+git://lila.oss/limb.git
diff --git a/meta/deps/limb/static b/meta/deps/limb/static
new file mode 100644
index 0000000..e69de29
diff --git a/meta/deps/limb/version b/meta/deps/limb/version
new file mode 100644
index 0000000..8acdd82
--- /dev/null
+++ b/meta/deps/limb/version
@@ -0,0 +1 @@
+0.0.1
diff --git a/meta/deps/skalibs/get_version b/meta/deps/skalibs/get_version
new file mode 100755
index 0000000..55041f5
--- /dev/null
+++ b/meta/deps/skalibs/get_version
@@ -0,0 +1,2 @@
+#!/bin/sh
+grep SKALIBS_VERSION "$@/skalibs/config.h" | cut -d\" -f2
diff --git a/meta/deps/skalibs/git b/meta/deps/skalibs/git
new file mode 100644
index 0000000..5965139
--- /dev/null
+++ b/meta/deps/skalibs/git
@@ -0,0 +1 @@
+git://lila.oss/skalibs.git
diff --git a/meta/deps/skalibs/incdir b/meta/deps/skalibs/incdir
new file mode 100644
index 0000000..e4484e2
--- /dev/null
+++ b/meta/deps/skalibs/incdir
@@ -0,0 +1 @@
+src/include
diff --git a/meta/deps/skalibs/include b/meta/deps/skalibs/include
new file mode 100644
index 0000000..e69de29
diff --git a/meta/deps/skalibs/libdir b/meta/deps/skalibs/libdir
new file mode 100644
index 0000000..5baacc7
--- /dev/null
+++ b/meta/deps/skalibs/libdir
@@ -0,0 +1 @@
+skalibs
diff --git a/meta/deps/skalibs/library b/meta/deps/skalibs/library
new file mode 100644
index 0000000..39971a0
--- /dev/null
+++ b/meta/deps/skalibs/library
@@ -0,0 +1 @@
+skarnet
diff --git a/meta/deps/skalibs/version b/meta/deps/skalibs/version
new file mode 100644
index 0000000..0ab9020
--- /dev/null
+++ b/meta/deps/skalibs/version
@@ -0,0 +1 @@
+2.8.0.0
diff --git a/project.mk b/project.mk
index 0892fd9..5b56fc0 100644
--- a/project.mk
+++ b/project.mk
@@ -1,7 +1,4 @@
-$(error You need to edit project.mk)
-
 # binaries: -- don't forget to set meta/deps-bin with all deps & .o files
-BINS = 
-
-# librairies -- don't forget to set meta/deps-lib with all deps & .o files
-LIBS = 
+BINS = test-sha1-ska test-sha256-ska test-blake2s-ska \
+	   test-sha3-lila test-blake3-lila \
+	   test-sha3
diff --git a/src/blake2s-ska.c b/src/blake2s-ska.c
new file mode 100644
index 0000000..322c616
--- /dev/null
+++ b/src/blake2s-ska.c
@@ -0,0 +1,23 @@
+#include <skalibs/stdcrypto.h>
+
+static blake2s_ctx ctx;
+
+void init(void)
+{
+    blake2s_init(&ctx, 32);
+}
+
+void update(const char *msg, size_t size)
+{
+    blake2s_update(&ctx, msg, size);
+}
+
+void final(unsigned char *md)
+{
+    blake2s_final(&ctx, (char *) md);
+}
+
+int hashlen(void)
+{
+    return 32;
+}
diff --git a/src/blake3-lila.c b/src/blake3-lila.c
new file mode 100644
index 0000000..36a8ae5
--- /dev/null
+++ b/src/blake3-lila.c
@@ -0,0 +1,23 @@
+#include <limb/blake3.h>
+
+blake3_ctx ctx;
+
+void init(void)
+{
+    blake3_init(&ctx);
+}
+
+void update(const char *msg, size_t size)
+{
+    blake3_update(&ctx, msg, size);
+}
+
+void final(unsigned char *md)
+{
+    blake3_final(&ctx, md);
+}
+
+int hashlen(void)
+{
+    return 32;
+}
diff --git a/src/sha1-ska.c b/src/sha1-ska.c
new file mode 100644
index 0000000..b7636ef
--- /dev/null
+++ b/src/sha1-ska.c
@@ -0,0 +1,23 @@
+#include <skalibs/stdcrypto.h>
+
+static SHA1Schedule ctx;
+
+void init(void)
+{
+    sha1_init(&ctx);
+}
+
+void update(const char *msg, size_t size)
+{
+    sha1_update(&ctx, msg, size);
+}
+
+void final(unsigned char *md)
+{
+    sha1_final(&ctx, (char *) md);
+}
+
+int hashlen(void)
+{
+    return 20;
+}
diff --git a/src/sha256-ska.c b/src/sha256-ska.c
new file mode 100644
index 0000000..585fef2
--- /dev/null
+++ b/src/sha256-ska.c
@@ -0,0 +1,23 @@
+#include <skalibs/stdcrypto.h>
+
+static SHA256Schedule ctx;
+
+void init(void)
+{
+    sha256_init(&ctx);
+}
+
+void update(const char *msg, size_t size)
+{
+    sha256_update(&ctx, msg, size);
+}
+
+void final(unsigned char *md)
+{
+    sha256_final(&ctx, (char *) md);
+}
+
+int hashlen(void)
+{
+    return 32;
+}
diff --git a/src/sha3-impl.c b/src/sha3-impl.c
new file mode 100644
index 0000000..5a93c72
--- /dev/null
+++ b/src/sha3-impl.c
@@ -0,0 +1,325 @@
+/* -------------------------------------------------------------------------
+ * Works when compiled for either 32-bit or 64-bit targets, optimized for 
+ * 64 bit.
+ *
+ * Canonical implementation of Init/Update/Finalize for SHA-3 byte input. 
+ *
+ * SHA3-256, SHA3-384, SHA-512 are implemented. SHA-224 can easily be added.
+ *
+ * Based on code from http://keccak.noekeon.org/ .
+ *
+ * I place the code that I wrote into public domain, free to use. 
+ *
+ * I would appreciate if you give credits to this work if you used it to 
+ * write or test * your code.
+ *
+ * Aug 2015. Andrey Jivsov. crypto@brainhub.org
+ * ---------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "sha3-impl.h"
+
+#define SHA3_ASSERT( x )
+#define SHA3_TRACE( format, ...)
+#define SHA3_TRACE_BUF(format, buf, l)
+
+/* 
+ * This flag is used to configure "pure" Keccak, as opposed to NIST SHA3.
+ */
+#define SHA3_USE_KECCAK_FLAG 0x80000000
+#define SHA3_CW(x) ((x) & (~SHA3_USE_KECCAK_FLAG))
+
+
+#if defined(_MSC_VER)
+#define SHA3_CONST(x) x
+#else
+#define SHA3_CONST(x) x##L
+#endif
+
+#ifndef SHA3_ROTL64
+#define SHA3_ROTL64(x, y) \
+	(((x) << (y)) | ((x) >> ((sizeof(uint64_t)*8) - (y))))
+#endif
+
+static const uint64_t keccakf_rndc[24] = {
+    SHA3_CONST(0x0000000000000001UL), SHA3_CONST(0x0000000000008082UL),
+    SHA3_CONST(0x800000000000808aUL), SHA3_CONST(0x8000000080008000UL),
+    SHA3_CONST(0x000000000000808bUL), SHA3_CONST(0x0000000080000001UL),
+    SHA3_CONST(0x8000000080008081UL), SHA3_CONST(0x8000000000008009UL),
+    SHA3_CONST(0x000000000000008aUL), SHA3_CONST(0x0000000000000088UL),
+    SHA3_CONST(0x0000000080008009UL), SHA3_CONST(0x000000008000000aUL),
+    SHA3_CONST(0x000000008000808bUL), SHA3_CONST(0x800000000000008bUL),
+    SHA3_CONST(0x8000000000008089UL), SHA3_CONST(0x8000000000008003UL),
+    SHA3_CONST(0x8000000000008002UL), SHA3_CONST(0x8000000000000080UL),
+    SHA3_CONST(0x000000000000800aUL), SHA3_CONST(0x800000008000000aUL),
+    SHA3_CONST(0x8000000080008081UL), SHA3_CONST(0x8000000000008080UL),
+    SHA3_CONST(0x0000000080000001UL), SHA3_CONST(0x8000000080008008UL)
+};
+
+static const unsigned keccakf_rotc[24] = {
+    1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62,
+    18, 39, 61, 20, 44
+};
+
+static const unsigned keccakf_piln[24] = {
+    10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20,
+    14, 22, 9, 6, 1
+};
+
+/* generally called after SHA3_KECCAK_SPONGE_WORDS-ctx->capacityWords words 
+ * are XORed into the state s 
+ */
+static void
+keccakf(uint64_t s[25])
+{
+    int i, j, round;
+    uint64_t t, bc[5];
+#define KECCAK_ROUNDS 24
+
+    for(round = 0; round < KECCAK_ROUNDS; round++) {
+
+        /* Theta */
+        for(i = 0; i < 5; i++)
+            bc[i] = s[i] ^ s[i + 5] ^ s[i + 10] ^ s[i + 15] ^ s[i + 20];
+
+        for(i = 0; i < 5; i++) {
+            t = bc[(i + 4) % 5] ^ SHA3_ROTL64(bc[(i + 1) % 5], 1);
+            for(j = 0; j < 25; j += 5)
+                s[j + i] ^= t;
+        }
+
+        /* Rho Pi */
+        t = s[1];
+        for(i = 0; i < 24; i++) {
+            j = keccakf_piln[i];
+            bc[0] = s[j];
+            s[j] = SHA3_ROTL64(t, keccakf_rotc[i]);
+            t = bc[0];
+        }
+
+        /* Chi */
+        for(j = 0; j < 25; j += 5) {
+            for(i = 0; i < 5; i++)
+                bc[i] = s[j + i];
+            for(i = 0; i < 5; i++)
+                s[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5];
+        }
+
+        /* Iota */
+        s[0] ^= keccakf_rndc[round];
+    }
+}
+
+/* *************************** Public Inteface ************************ */
+
+/* For Init or Reset call these: */
+sha3_return_t
+sha3_Init(void *priv, unsigned bitSize) {
+    sha3_context *ctx = (sha3_context *) priv;
+    /*
+    if( bitSize != 256 && bitSize != 384 && bitSize != 512 )
+        return SHA3_RETURN_BAD_PARAMS;
+        */
+    memset(ctx, 0, sizeof(*ctx));
+    ctx->capacityWords = 2 * bitSize / (8 * sizeof(uint64_t));
+    return SHA3_RETURN_OK;
+}
+
+void
+sha3_Init256(void *priv)
+{
+    sha3_Init(priv, 256);
+}
+
+void
+sha3_Init384(void *priv)
+{
+    sha3_Init(priv, 384);
+}
+
+void
+sha3_Init512(void *priv)
+{
+    sha3_Init(priv, 512);
+}
+
+enum SHA3_FLAGS
+sha3_SetFlags(void *priv, enum SHA3_FLAGS flags)
+{
+    sha3_context *ctx = (sha3_context *) priv;
+    flags &= SHA3_FLAGS_KECCAK;
+    ctx->capacityWords |= (flags == SHA3_FLAGS_KECCAK ? SHA3_USE_KECCAK_FLAG : 0);
+    return flags;
+}
+
+
+void
+sha3_Update(void *priv, void const *bufIn, size_t len)
+{
+    sha3_context *ctx = (sha3_context *) priv;
+
+    /* 0...7 -- how much is needed to have a word */
+    unsigned old_tail = (8 - ctx->byteIndex) & 7;
+
+    size_t words;
+    unsigned tail;
+    size_t i;
+
+    const uint8_t *buf = bufIn;
+
+    SHA3_TRACE_BUF("called to update with:", buf, len);
+
+    SHA3_ASSERT(ctx->byteIndex < 8);
+    SHA3_ASSERT(ctx->wordIndex < sizeof(ctx->u.s) / sizeof(ctx->u.s[0]));
+
+    if(len < old_tail) {        /* have no complete word or haven't started 
+                                 * the word yet */
+        SHA3_TRACE("because %d<%d, store it and return", (unsigned)len,
+                (unsigned)old_tail);
+        /* endian-independent code follows: */
+        while (len--)
+            ctx->saved |= (uint64_t) (*(buf++)) << ((ctx->byteIndex++) * 8);
+        SHA3_ASSERT(ctx->byteIndex < 8);
+        return;
+    }
+
+    if(old_tail) {              /* will have one word to process */
+        SHA3_TRACE("completing one word with %d bytes", (unsigned)old_tail);
+        /* endian-independent code follows: */
+        len -= old_tail;
+        while (old_tail--)
+            ctx->saved |= (uint64_t) (*(buf++)) << ((ctx->byteIndex++) * 8);
+
+        /* now ready to add saved to the sponge */
+        ctx->u.s[ctx->wordIndex] ^= ctx->saved;
+        SHA3_ASSERT(ctx->byteIndex == 8);
+        ctx->byteIndex = 0;
+        ctx->saved = 0;
+        if(++ctx->wordIndex ==
+                (SHA3_KECCAK_SPONGE_WORDS - SHA3_CW(ctx->capacityWords))) {
+            keccakf(ctx->u.s);
+            ctx->wordIndex = 0;
+        }
+    }
+
+    /* now work in full words directly from input */
+
+    SHA3_ASSERT(ctx->byteIndex == 0);
+
+    words = len / sizeof(uint64_t);
+    tail = len - words * sizeof(uint64_t);
+
+    SHA3_TRACE("have %d full words to process", (unsigned)words);
+
+    for(i = 0; i < words; i++, buf += sizeof(uint64_t)) {
+        const uint64_t t = (uint64_t) (buf[0]) |
+                ((uint64_t) (buf[1]) << 8 * 1) |
+                ((uint64_t) (buf[2]) << 8 * 2) |
+                ((uint64_t) (buf[3]) << 8 * 3) |
+                ((uint64_t) (buf[4]) << 8 * 4) |
+                ((uint64_t) (buf[5]) << 8 * 5) |
+                ((uint64_t) (buf[6]) << 8 * 6) |
+                ((uint64_t) (buf[7]) << 8 * 7);
+#if defined(__x86_64__ ) || defined(__i386__)
+        SHA3_ASSERT(memcmp(&t, buf, 8) == 0);
+#endif
+        ctx->u.s[ctx->wordIndex] ^= t;
+        if(++ctx->wordIndex ==
+                (SHA3_KECCAK_SPONGE_WORDS - SHA3_CW(ctx->capacityWords))) {
+            keccakf(ctx->u.s);
+            ctx->wordIndex = 0;
+        }
+    }
+
+    SHA3_TRACE("have %d bytes left to process, save them", (unsigned)tail);
+
+    /* finally, save the partial word */
+    SHA3_ASSERT(ctx->byteIndex == 0 && tail < 8);
+    while (tail--) {
+        SHA3_TRACE("Store byte %02x '%c'", *buf, *buf);
+        ctx->saved |= (uint64_t) (*(buf++)) << ((ctx->byteIndex++) * 8);
+    }
+    SHA3_ASSERT(ctx->byteIndex < 8);
+    SHA3_TRACE("Have saved=0x%016" PRIx64 " at the end", ctx->saved);
+}
+
+/* This is simply the 'update' with the padding block.
+ * The padding block is 0x01 || 0x00* || 0x80. First 0x01 and last 0x80 
+ * bytes are always present, but they can be the same byte.
+ */
+void const *
+sha3_Finalize(void *priv)
+{
+    sha3_context *ctx = (sha3_context *) priv;
+
+    SHA3_TRACE("called with %d bytes in the buffer", ctx->byteIndex);
+
+    /* Append 2-bit suffix 01, per SHA-3 spec. Instead of 1 for padding we
+     * use 1<<2 below. The 0x02 below corresponds to the suffix 01.
+     * Overall, we feed 0, then 1, and finally 1 to start padding. Without
+     * M || 01, we would simply use 1 to start padding. */
+
+    uint64_t t;
+
+    if( ctx->capacityWords & SHA3_USE_KECCAK_FLAG ) {
+        /* Keccak version */
+        t = (uint64_t)(((uint64_t) 1) << (ctx->byteIndex * 8));
+    }
+    else {
+        /* SHA3 version */
+        t = (uint64_t)(((uint64_t)(0x02 | (1 << 2))) << ((ctx->byteIndex) * 8));
+    }
+
+    ctx->u.s[ctx->wordIndex] ^= ctx->saved ^ t;
+
+    ctx->u.s[SHA3_KECCAK_SPONGE_WORDS - SHA3_CW(ctx->capacityWords) - 1] ^=
+            SHA3_CONST(0x8000000000000000UL);
+    keccakf(ctx->u.s);
+
+    /* Return first bytes of the ctx->s. This conversion is not needed for
+     * little-endian platforms e.g. wrap with #if !defined(__BYTE_ORDER__)
+     * || !defined(__ORDER_LITTLE_ENDIAN__) || __BYTE_ORDER__!=__ORDER_LITTLE_ENDIAN__ 
+     *    ... the conversion below ...
+     * #endif */
+    {
+        unsigned i;
+        for(i = 0; i < SHA3_KECCAK_SPONGE_WORDS; i++) {
+            const unsigned t1 = (uint32_t) ctx->u.s[i];
+            const unsigned t2 = (uint32_t) ((ctx->u.s[i] >> 16) >> 16);
+            ctx->u.sb[i * 8 + 0] = (uint8_t) (t1);
+            ctx->u.sb[i * 8 + 1] = (uint8_t) (t1 >> 8);
+            ctx->u.sb[i * 8 + 2] = (uint8_t) (t1 >> 16);
+            ctx->u.sb[i * 8 + 3] = (uint8_t) (t1 >> 24);
+            ctx->u.sb[i * 8 + 4] = (uint8_t) (t2);
+            ctx->u.sb[i * 8 + 5] = (uint8_t) (t2 >> 8);
+            ctx->u.sb[i * 8 + 6] = (uint8_t) (t2 >> 16);
+            ctx->u.sb[i * 8 + 7] = (uint8_t) (t2 >> 24);
+        }
+    }
+
+    SHA3_TRACE_BUF("Hash: (first 32 bytes)", ctx->u.sb, 256 / 8);
+
+    return (ctx->u.sb);
+}
+
+sha3_return_t sha3_HashBuffer( unsigned bitSize, enum SHA3_FLAGS flags, const void *in, unsigned inBytes, void *out, unsigned outBytes ) {
+    sha3_return_t err;
+    sha3_context c;
+
+    err = sha3_Init(&c, bitSize);
+    if( err != SHA3_RETURN_OK )
+        return err;
+    if( sha3_SetFlags(&c, flags) != flags ) {
+        return SHA3_RETURN_BAD_PARAMS;
+    }
+    sha3_Update(&c, in, inBytes);
+    const void *h = sha3_Finalize(&c);
+
+    if(outBytes > bitSize/8)
+        outBytes = bitSize/8;
+    memcpy(out, h, outBytes);
+    return SHA3_RETURN_OK;
+}
diff --git a/src/sha3-lila.c b/src/sha3-lila.c
new file mode 100644
index 0000000..e3024b4
--- /dev/null
+++ b/src/sha3-lila.c
@@ -0,0 +1,23 @@
+#include <limb/sha3.h>
+
+static sha3_ctx ctx;
+
+void init(void)
+{
+    sha3_init(&ctx, 256);
+}
+
+void update(const char *msg, size_t size)
+{
+    sha3_update(&ctx, msg, size);
+}
+
+void final(unsigned char *md)
+{
+    sha3_final(&ctx, md);
+}
+
+int hashlen(void)
+{
+    return 32;
+}
diff --git a/src/sha3.c b/src/sha3.c
new file mode 100644
index 0000000..573af1f
--- /dev/null
+++ b/src/sha3.c
@@ -0,0 +1,24 @@
+#include "sha3-impl.h"
+
+sha3_context ctx;
+
+void init(void)
+{
+    sha3_Init256(&ctx);
+}
+
+void update(const char *msg, size_t size)
+{
+    sha3_Update(&ctx, msg, size);
+}
+
+void final(unsigned char *md)
+{
+    const char *md_ = sha3_Finalize(&ctx);
+    memcpy(md, md_, 32);
+}
+
+int hashlen(void)
+{
+    return 32;
+}
diff --git a/src/test.c b/src/test.c
new file mode 100644
index 0000000..97c4c8e
--- /dev/null
+++ b/src/test.c
@@ -0,0 +1,58 @@
+#include <time.h>
+#include <stdio.h>
+
+#include <skalibs/buffer.h>
+#include <skalibs/fmtscan.h>
+static void
+dump(const unsigned char *data, size_t dlen, int compact)
+{
+    char buf[3];
+    buf[2] = ' ';
+    int lf = compact < 2;
+    compact = (compact) ? 1 : 0;
+
+    size_t i;
+    for (i = 0; i < dlen; ) {
+        buf[1] = fmtscan_asc((unsigned char) data[i] % 16);
+        buf[0] = fmtscan_asc((unsigned char) data[i] / 16);
+        buffer_put(buffer_1, buf, 2 + !compact);
+        if (!(++i % 8) && !compact) buffer_put(buffer_1, buf + 2, 1);
+        if (!compact && !(i % 16)) buffer_put(buffer_1, "\n", 1);
+    }
+    if (lf && (compact || i % 16))
+        buffer_put(buffer_1, "\n", 1);
+    buffer_flush(buffer_1);
+}
+
+extern void init(void);
+extern void update(const char *msg, size_t size);
+extern void final(unsigned char *md);
+extern int hashlen(void);
+
+int main(void)
+{
+    struct timespec ts1, ts2;
+    char msg[512 << 10];
+    unsigned char md[64] = { 0 };
+
+    for (int i = 512 << 10; i; --i)
+        msg[i] = i % 256;
+
+    clock_gettime(CLOCK_MONOTONIC, &ts1);
+    for (int iter = 0; iter < 100; ++iter) {
+        init();
+        for (int i = 0; i < 20; ++i)
+            update(msg, sizeof(msg));
+        final(md);
+    }
+    clock_gettime(CLOCK_MONOTONIC, &ts2);
+    dump(md, hashlen(), 1);
+
+    ts2.tv_sec -= ts1.tv_sec;
+    ts2.tv_nsec -= ts1.tv_nsec;
+    double took = ts2.tv_sec + (ts2.tv_nsec / 1000000000.0);
+    double speed = (((512 * 20) >> 10) * 100) / took;
+    printf("took %.09f seconds, hashing %f MiB/s\n", took, speed);
+
+    return 0;
+}
diff --git a/tests b/tests
new file mode 100755
index 0000000..a672035
--- /dev/null
+++ b/tests
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+test()
+{
+    for test do
+        echo - test ${test:5}
+        ./$test
+    done
+}
+
+test $(ls test-*)