aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Andrée <maandree@operamail.com>2015-07-25 17:08:25 +0200
committerMattias Andrée <maandree@operamail.com>2015-07-25 17:08:25 +0200
commit8b24277c444fdafa244c87d0ca5e3a7c47eaf76d (patch)
treee3770b0ab2a8bcd566555fb496800cf2982677d8
parentbump year (diff)
downloadlibkeccak-8b24277c444fdafa244c87d0ca5e3a7c47eaf76d.tar.gz
libkeccak-8b24277c444fdafa244c87d0ca5e3a7c47eaf76d.tar.bz2
libkeccak-8b24277c444fdafa244c87d0ca5e3a7c47eaf76d.tar.xz
beginning of hmac implementation
Signed-off-by: Mattias Andrée <maandree@operamail.com>
-rw-r--r--Makefile8
-rw-r--r--src/libkeccak.h1
-rw-r--r--src/libkeccak/mac/hmac.c273
-rw-r--r--src/libkeccak/mac/hmac.h366
-rw-r--r--src/libkeccak/state.h17
5 files changed, 654 insertions, 11 deletions
diff --git a/Makefile b/Makefile
index f502864..b8298b4 100644
--- a/Makefile
+++ b/Makefile
@@ -57,7 +57,7 @@ LDOPTIMISE =
FLAGS = -std=gnu99 $(WARN)
-LIB_OBJ = digest files generalised-spec hex state
+LIB_OBJ = digest files generalised-spec hex state mac/hmac
.PHONY: default
@@ -75,7 +75,7 @@ lib: so a
so: bin/libkeccak.so.$(LIB_VERSION) bin/libkeccak.so.$(LIB_MAJOR) bin/libkeccak.so
obj/libkeccak/%.o: src/libkeccak/%.c src/libkeccak.h src/libkeccak/*.h
- @mkdir -p obj/libkeccak
+ @mkdir -p $$(dirname $@)
$(CC) $(FLAGS) $(COPTIMISE) -fPIC -c -o $@ $< $(CFLAGS) $(CPPFLAGS)
bin/libkeccak.so.$(LIB_VERSION): $(foreach O,$(LIB_OBJ),obj/libkeccak/$(O).o)
@@ -155,6 +155,7 @@ install-lib: install-headers install-dynamic-lib install-static-lib
install-headers:
install -dm755 -- "$(DESTDIR)$(INCLUDEDIR)"
install -dm755 -- "$(DESTDIR)$(INCLUDEDIR)/libkeccak"
+ install -dm755 -- "$(DESTDIR)$(INCLUDEDIR)/libkeccak/mac"
install -m644 -- src/libkeccak.h "$(DESTDIR)$(INCLUDEDIR)/libkeccak.h"
install -m644 -- src/libkeccak/digest.h "$(DESTDIR)$(INCLUDEDIR)/libkeccak/digest.h"
install -m644 -- src/libkeccak/files.h "$(DESTDIR)$(INCLUDEDIR)/libkeccak/files.h"
@@ -162,6 +163,7 @@ install-headers:
install -m644 -- src/libkeccak/hex.h "$(DESTDIR)$(INCLUDEDIR)/libkeccak/hex.h"
install -m644 -- src/libkeccak/spec.h "$(DESTDIR)$(INCLUDEDIR)/libkeccak/spec.h"
install -m644 -- src/libkeccak/state.h "$(DESTDIR)$(INCLUDEDIR)/libkeccak/state.h"
+ install -m644 -- src/libkeccak/mac/hmac.h "$(DESTDIR)$(INCLUDEDIR)/libkeccak/mac/hmac.h"
.PHONY: install-dynamic-lib
install-dynamic-lib: bin/libkeccak.so.$(LIB_VERSION)
@@ -199,6 +201,8 @@ uninstall:
-rm -- "$(DESTDIR)$(INCLUDEDIR)/libkeccak/hex.h"
-rm -- "$(DESTDIR)$(INCLUDEDIR)/libkeccak/spec.h"
-rm -- "$(DESTDIR)$(INCLUDEDIR)/libkeccak/state.h"
+ -rm -- "$(DESTDIR)$(INCLUDEDIR)/libkeccak/mac/hmac.h"
+ -rmdir -- "$(DESTDIR)$(INCLUDEDIR)/libkeccak/mac"
-rmdir -- "$(DESTDIR)$(INCLUDEDIR)/libkeccak"
-rm -- "$(DESTDIR)$(LIBDIR)/libkeccak.so.$(LIB_VERSION)"
-rm -- "$(DESTDIR)$(LIBDIR)/libkeccak.so.$(LIB_MAJOR)"
diff --git a/src/libkeccak.h b/src/libkeccak.h
index 3812a6b..cedbc94 100644
--- a/src/libkeccak.h
+++ b/src/libkeccak.h
@@ -26,6 +26,7 @@
#include "libkeccak/digest.h"
#include "libkeccak/hex.h"
#include "libkeccak/files.h"
+#include "libkeccak/mac/hmac.h"
#endif
diff --git a/src/libkeccak/mac/hmac.c b/src/libkeccak/mac/hmac.c
new file mode 100644
index 0000000..b679e8e
--- /dev/null
+++ b/src/libkeccak/mac/hmac.c
@@ -0,0 +1,273 @@
+/**
+ * libkeccak – Keccak-family hashing library
+ *
+ * Copyright © 2014, 2015 Mattias Andrée (maandree@member.fsf.org)
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "hmac.h"
+
+#include "../digest.h"
+
+
+
+/**
+ * The outer pad pattern
+ */
+#define OUTER_PAD 0x5C
+
+/**
+ * The inner pad pattern
+ */
+#define INNER_PAD 0x36
+
+
+
+/**
+ * Change to HMAC-hashing key on the state
+ *
+ * @param state The state that should be reset
+ * @param key The new key
+ * @param key_length The length of key, in bits
+ * @return Zero on success, -1 on error
+ */
+int libkeccak_hmac_set_key(libkeccak_hmac_state_t* restrict state, const char* restrict key, size_t key_length)
+{
+ size_t i, size, new_key_length, key_bytes;
+ char* old;
+
+ size = (size_t)(state->sponge.r) > key_length ? (size_t)(state->sponge.r) : key_length;
+ new_key_length = size;
+ size = (size + 7) >> 3;
+ key_bytes = (key_length + 7) >> 3;
+
+ if (size != key_bytes)
+ {
+ state->key_opad = realloc(old = state->key_opad, 2 * size);
+ if (state->key_opad == NULL)
+ return state->key_opad = old, -1;
+ state->key_ipad = state->key_opad + size / sizeof(char);
+ }
+
+ memcpy(state->key_opad, key, key_bytes);
+ if (key_length & 7)
+ state->key_opad[(key_bytes >> 3) - 1] &= (1 << (key_length & 7)) - 1;
+
+ if ((size_t)(state->sponge.r) > key_length)
+ __builtin_memset(state->key_opad + key_bytes / sizeof(char), 0, size - key_bytes);
+
+ for (i = 0; i < size; i++)
+ state->key_ipad[i] = state->key_opad[i] ^ INNER_PAD,
+ state->key_opad[i] ^= OUTER_PAD;
+
+ state->key_length = new_key_length;
+
+ return 0;
+}
+
+
+/**
+ * Wipe sensitive data wihout freeing any data
+ *
+ * @param state The state that should be wipe
+ */
+void libkeccak_hmac_wipe(volatile libkeccak_hmac_state_t* restrict state)
+{
+ volatile char* restrict key_pads;
+ size_t i, size;
+ key_pads = state->key_opad;
+ size = 2 * ((state->key_length + 7) >> 3);
+ libkeccak_state_wipe(&(state->sponge));
+ for (i = 0; i < size; i++)
+ key_pads[i] = 0;
+ state->leftover = 0;
+ __builtin_memset(state->buffer, 0, state->buffer_size);
+}
+
+
+/**
+ * Make a copy of an HMAC hashing-state
+ *
+ * @param dest The slot for the duplicate, must not be initialised (memory leak otherwise)
+ * @param src The state to duplicate
+ * @return Zero on success, -1 on error
+ */
+int libkeccak_hmac_copy(libkeccak_hmac_state_t* restrict dest, const libkeccak_hmac_state_t* restrict src)
+{
+ int saved_errno;
+ size_t size;
+
+ dest->key_opad = NULL;
+ dest->key_ipad = NULL;
+
+ if (libkeccak_state_copy(&(dest->sponge), &(src->sponge)) < 0)
+ return -1;
+
+ dest->key_length = src->key_length;
+ dest->leftover = src->leftover;
+
+ size = (src->key_length + 7) >> 3;
+ dest->key_opad = malloc(2 * size);
+ if (dest->key_opad == NULL)
+ return saved_errno = errno, libkeccak_state_destroy(&(dest->sponge)), errno = saved_errno, -1;
+ dest->key_ipad = dest->key_opad + size / sizeof(char);
+
+ memcpy(dest->key_opad, src->key_opad, size);
+ memcpy(dest->key_ipad, src->key_ipad, size);
+
+ return 0;
+}
+
+
+/**
+ * Unmarshal a `libkeccak_hmac_state_t` from a buffer
+ *
+ * @param state The slot for the unmarshalled state, must not be initialised (memory leak otherwise)
+ * @param data The input buffer
+ * @return The number of bytes read from `data`, 0 on error
+ */
+size_t libkeccak_hmac_unmarshal(libkeccak_hmac_state_t* restrict state, const char* restrict data)
+{
+ size_t parsed, size, i;
+ int saved_errno;
+
+ state->key_opad = NULL;
+ state->key_ipad = NULL;
+
+ parsed = libkeccak_state_unmarshal(&(state->sponge), data);
+ if (parsed == 0)
+ return 0;
+
+ data += parsed / sizeof(char);
+ state->key_length = *(const size_t*)data;
+ data += sizeof(size_t) / sizeof(char);
+ size = (state->key_length + 7) >> 3;
+
+ state->key_opad = malloc(2 * size);
+ if (state->key_opad == NULL)
+ return saved_errno = errno, libkeccak_state_destroy(&(state->sponge)), errno = saved_errno, -1;
+ memcpy(state->key_opad, data, size);
+ data += size / sizeof(char);
+
+ if (data[0])
+ {
+ state->key_ipad = state->key_opad + size / sizeof(char);
+ memcpy(state->key_ipad, state->key_opad, size);
+ for (i = 0; i < size / sizeof(char); i++)
+ state->key_ipad[i] ^= (char)(OUTER_PAD ^ INNER_PAD);
+ }
+
+ state->leftover = data[1];
+ state->buffer = NULL;
+ state->buffer_size = 0;
+
+ return parsed + sizeof(size_t) + size + 2 * sizeof(char);
+}
+
+
+/**
+ * Absorb more, or the first part, of the message
+ * without wiping sensitive data when possible
+ *
+ * @param state The hashing state
+ * @param msg The partial message
+ * @param msglen The length of the partial message, in bytes
+ * @return Zero on success, -1 on error
+ */
+__attribute__((nonnull))
+int libkeccak_hmac_fast_update(libkeccak_hmac_state_t* restrict state, const char* restrict msg, size_t msglen)
+{
+ char* old;
+ size_t i;
+ int n, cn;
+
+ if (state->key_ipad != NULL)
+ {
+ if (libkeccak_fast_update(&(state->sponge), state->key_ipad, state->key_length >> 3) < 0)
+ return -1;
+ if (state->key_length & 7)
+ state->leftover = state->key_ipad[(state->key_length >> 3)];
+ state->key_ipad = NULL;
+ }
+
+ if (!(state->key_length & 7))
+ return libkeccak_fast_update(&(state->sponge), msg, msglen);
+
+ if (msglen != state->buffer_size)
+ {
+ state->buffer = realloc(old = state->buffer, state->buffer_size = msglen);
+ if (state->buffer == NULL)
+ return state->buffer = old, -1;
+ }
+
+ n = (int)(state->key_length & 7);
+ cn = 8 - n;
+ for (i = 1; i < msglen; i++)
+ state->buffer[i] = (((unsigned char)(msg[i - 1])) >> cn) | (msg[i] << n);
+ state->buffer[0] = (state->leftover & ((1 << n) - 1)) | (msg[0] << n);
+ state->leftover = ((unsigned char)(msg[msglen - 1])) >> cn;
+
+ return libkeccak_fast_update(&(state->sponge), state->buffer, msglen);
+}
+
+
+/**
+ * Absorb more, or the first part, of the message
+ * and wipe sensitive data when possible
+ *
+ * @param state The hashing state
+ * @param msg The partial message
+ * @param msglen The length of the partial message, in bytes
+ * @return Zero on success, -1 on error
+ */
+__attribute__((nonnull))
+int libkeccak_hmac_update(libkeccak_hmac_state_t* restrict state, const char* restrict msg, size_t msglen)
+{
+ size_t i;
+ int n, cn, r, saved_errno;
+
+ if (state->key_ipad != NULL)
+ {
+ if (libkeccak_update(&(state->sponge), state->key_ipad, state->key_length >> 3) < 0)
+ return -1;
+ if (state->key_length & 7)
+ state->leftover = state->key_ipad[(state->key_length >> 3)];
+ state->key_ipad = NULL;
+ }
+
+ if (!(state->key_length & 7))
+ return libkeccak_update(&(state->sponge), msg, msglen);
+
+ if (msglen != state->buffer_size)
+ {
+ free(state->buffer);
+ state->buffer = malloc(state->buffer_size = msglen);
+ if (state->buffer == NULL)
+ return -1;
+ }
+
+ n = (int)(state->key_length & 7);
+ cn = 8 - n;
+ for (i = 1; i < msglen; i++)
+ state->buffer[i] = (((unsigned char)(msg[i - 1])) >> cn) | (msg[i] << n);
+ state->buffer[0] = (state->leftover & ((1 << n) - 1)) | (msg[0] << n);
+ state->leftover = ((unsigned char)(msg[msglen - 1])) >> cn;
+
+ r = libkeccak_update(&(state->sponge), state->buffer, msglen);
+ saved_errno = errno;
+ __builtin_memset(state->buffer, 0, msglen);
+ errno = saved_errno;
+ return r;
+}
+
diff --git a/src/libkeccak/mac/hmac.h b/src/libkeccak/mac/hmac.h
new file mode 100644
index 0000000..9526ef8
--- /dev/null
+++ b/src/libkeccak/mac/hmac.h
@@ -0,0 +1,366 @@
+/**
+ * libkeccak – Keccak-family hashing library
+ *
+ * Copyright © 2014, 2015 Mattias Andrée (maandree@member.fsf.org)
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef LIBKECCAK_MAC_HMAC_H
+#define LIBKECCAK_MAC_HMAC_H 1
+
+
+/* The Keccak hash-function, that was selected by NIST as the SHA-3 competition winner,
+ * doesn't need this nested approach and can be used to generate a MAC by simply prepending
+ * the key to the message. [http://keccak.noekeon.org]
+ */
+
+
+#include "../spec.h"
+#include "../state.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+
+
+/**
+ * Datastructure that describes the state of an HMAC-hashing process
+ */
+typedef struct libkeccak_hmac_state
+{
+ /**
+ * The key right-padded and XOR:ed with the outer pad
+ */
+ char* restrict key_opad;
+
+ /**
+ * The key right-padded and XOR:ed with the inner pad
+ */
+ char* restrict key_ipad;
+ /* Not marshalled, implicitly unmarshalled using `key_opad`. */
+ /* Shares allocation with `key_opad`, do not `free`. */
+
+ /**
+ * The length of key, but at least the input block size, in bits
+ */
+ size_t key_length;
+
+ /**
+ * The state of the underlaying hash-algorithm
+ */
+ libkeccak_state_t sponge;
+
+ /**
+ * Buffer used to temporarily store bit shift message if
+ * `key_length` is not zero modulus 8
+ */
+ char* restrict buffer;
+
+ /**
+ * The allocation size of `buffer`
+ */
+ size_t buffer_size;
+
+ /**
+ * Part of feed key, message or digest that have not been passed yet
+ */
+ char leftover;
+
+ char __pad[sizeof(void*) / sizeof(char) - 1];
+
+} libkeccak_hmac_state_t;
+
+
+
+/**
+ * Change to HMAC-hashing key on the state
+ *
+ * @param state The state that should be reset
+ * @param key The new key
+ * @param key_length The length of key, in bits
+ * @return Zero on success, -1 on error
+ */
+__attribute__((nonnull(1), unused))
+int libkeccak_hmac_set_key(libkeccak_hmac_state_t* restrict state, const char* restrict key, size_t key_length);
+
+
+/**
+ * Initialise an HMAC hashing-state according to hashing specifications
+ *
+ * @param state The state that should be initialised
+ * @param spec The specifications for the state
+ * @param key The key
+ * @param key_length The length of key, in bits
+ * @return Zero on success, -1 on error
+ */
+static inline __attribute__((nonnull))
+int libkeccak_hmac_initialise(libkeccak_hmac_state_t* restrict state, const libkeccak_spec_t* restrict spec,
+ const char* restrict key, size_t key_length)
+{
+ int saved_errno;
+ if (libkeccak_state_initialise(&(state->sponge), spec) < 0)
+ return -1;
+ if (libkeccak_hmac_set_key(state, key, key_length) < 0)
+ return saved_errno = errno, libkeccak_state_destroy(&(state->sponge)), errno = saved_errno, -1;
+ state->leftover = 0;
+ state->buffer = NULL;
+ state->buffer_size = 0;
+ return 0;
+}
+
+
+/**
+ * Wrapper for `libkeccak_hmac_initialise` that also allocates the states
+ *
+ * @param spec The specifications for the state
+ * @param key The key
+ * @param key_length The length of key, in bits
+ * @return The state, `NULL` on error
+ */
+static inline __attribute__((nonnull, unused, warn_unused_result))
+libkeccak_hmac_state_t* libkeccak_hmac_create(const libkeccak_spec_t* restrict spec,
+ const char* restrict key, size_t key_length)
+{
+ libkeccak_hmac_state_t* restrict state = malloc(sizeof(libkeccak_hmac_state_t));
+ int saved_errno;
+ if ((state == NULL) || libkeccak_hmac_initialise(state, spec, key, key_length))
+ return saved_errno = errno, free(state), errno = saved_errno, NULL;
+ return state;
+}
+
+
+/**
+ * Reset an HMAC-hashing state according to hashing specifications,
+ * you can choose whether to change the key
+ *
+ * @param state The state that should be reset
+ * @param key The new key, `NULL` to keep the old key
+ * @param key_length The length of key, in bits, ignored if `key == NULL`
+ * @return Zero on success, -1 on error
+ */
+static inline __attribute__((nonnull(1), unused))
+int libkeccak_hmac_reset(libkeccak_hmac_state_t* restrict state, const char* restrict key, size_t key_length)
+{
+ libkeccak_state_reset(&(state->sponge));
+ return key != NULL ? libkeccak_hmac_set_key(state, key, key_length) : 0;
+}
+
+
+/**
+ * Wipe sensitive data wihout freeing any data
+ *
+ * @param state The state that should be wipe
+ */
+__attribute__((nonnull, nothrow, optimize("-O0")))
+void libkeccak_hmac_wipe(volatile libkeccak_hmac_state_t* restrict state);
+
+
+/**
+ * Release resources allocation for an HMAC hashing-state without wiping sensitive data
+ *
+ * @param state The state that should be destroyed
+ */
+static inline
+void libkeccak_hmac_fast_destroy(libkeccak_hmac_state_t* restrict state)
+{
+ if (state == NULL)
+ return;
+ free(state->key_opad);
+ state->key_opad = NULL;
+ state->key_ipad = NULL;
+ state->key_length = 0;
+ free(state->buffer);
+ state->buffer = NULL;
+ state->buffer_size = 0;
+}
+
+
+/**
+ * Release resources allocation for an HMAC hasing-state and wipe sensitive data
+ *
+ * @param state The state that should be destroyed
+ */
+static inline __attribute__((unused, optimize("-O0")))
+void libkeccak_hmac_destroy(volatile libkeccak_hmac_state_t* restrict state)
+{
+ if (state == NULL)
+ return;
+ libkeccak_hmac_wipe(state);
+ free(state->key_opad);
+ state->key_opad = NULL;
+ state->key_ipad = NULL;
+ state->key_length = 0;
+ state->leftover = 0;
+ free(state->buffer);
+ state->buffer = NULL;
+ state->buffer_size = 0;
+}
+
+
+/**
+ * Wrapper for `libkeccak_fast_destroy` that also frees the allocation of the state
+ *
+ * @param state The state that should be freed
+ */
+static inline __attribute__((unused))
+void libkeccak_hmac_fast_free(libkeccak_hmac_state_t* restrict state)
+{
+ libkeccak_hmac_fast_destroy(state);
+ free(state);
+}
+
+
+/**
+ * Wrapper for `libkeccak_hmac_destroy` that also frees the allocation of the state
+ *
+ * @param state The state that should be freed
+ */
+static inline __attribute__((unused, optimize("-O0")))
+void libkeccak_hmac_free(volatile libkeccak_hmac_state_t* restrict state)
+{
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wcast-qual"
+ libkeccak_hmac_destroy(state);
+ free((libkeccak_hmac_state_t*)state);
+# pragma GCC diagnostic pop
+}
+
+
+/**
+ * Make a copy of an HMAC hashing-state
+ *
+ * @param dest The slot for the duplicate, must not be initialised (memory leak otherwise)
+ * @param src The state to duplicate
+ * @return Zero on success, -1 on error
+ */
+__attribute__((nonnull))
+int libkeccak_hmac_copy(libkeccak_hmac_state_t* restrict dest, const libkeccak_hmac_state_t* restrict src);
+
+
+/**
+ * A wrapper for `libkeccak_hmac_copy` that also allocates the duplicate
+ *
+ * @param src The state to duplicate
+ * @return The duplicate, `NULL` on error
+ */
+static inline __attribute__((nonnull, unused, warn_unused_result))
+libkeccak_hmac_state_t* libkeccak_hmac_duplicate(const libkeccak_hmac_state_t* restrict src)
+{
+ libkeccak_hmac_state_t* restrict dest = malloc(sizeof(libkeccak_hmac_state_t));
+ int saved_errno;
+ if ((dest == NULL) || libkeccak_hmac_copy(dest, src))
+ return saved_errno = errno, libkeccak_hmac_free(dest), errno = saved_errno, NULL;
+ return dest;
+}
+
+
+/**
+ * Calculates the allocation size required for the second argument
+ * of `libkeccak_hmac_marshal` (`char* restrict data)`)
+ *
+ * @param state The state as it will be marshalled by a subsequent call to `libkeccak_hamc_marshal`
+ * @return The allocation size needed for the buffer to which the state will be marshalled
+ */
+static inline __attribute__((nonnull, nothrow, unused, warn_unused_result, pure))
+size_t libkeccak_hmac_marshal_size(const libkeccak_hmac_state_t* restrict state)
+{
+ return libkeccak_state_marshal_size(&(state->sponge)) + sizeof(size_t) +
+ ((state->key_length + 7) >> 3) + 2 * sizeof(char);
+}
+
+
+/**
+ * Marshal a `libkeccak_hmac_state_t` into a buffer
+ *
+ * @param state The state to marshal
+ * @param data The output buffer
+ * @return The number of bytes stored to `data`
+ */
+static inline __attribute__((nonnull, nothrow))
+size_t libkeccak_hmac_marshal(const libkeccak_hmac_state_t* restrict state, char* restrict data)
+{
+ size_t written = libkeccak_state_marshal(&(state->sponge), data);
+ data += written / sizeof(char);
+ *(size_t*)data = state->key_length;
+ data += sizeof(size_t) / sizeof(char);
+ memcpy(data, state->key_opad, (state->key_length + 7) >> 3);
+ data += ((state->key_length + 7) >> 3) / sizeof(char);
+ data[0] = (char)(state->key_ipad != NULL);
+ data[1] = state->leftover;
+ return written + sizeof(size_t) + ((state->key_length + 7) >> 3) + 2 * sizeof(char);
+}
+
+
+/**
+ * Unmarshal a `libkeccak_hmac_state_t` from a buffer
+ *
+ * @param state The slot for the unmarshalled state, must not be initialised (memory leak otherwise)
+ * @param data The input buffer
+ * @return The number of bytes read from `data`, 0 on error
+ */
+__attribute__((nonnull))
+size_t libkeccak_hmac_unmarshal(libkeccak_hmac_state_t* restrict state, const char* restrict data);
+
+
+/**
+ * Gets the number of bytes the `libkeccak_hmac_state_t` stored
+ * at the beginning of `data` occupies
+ *
+ * @param data The data buffer
+ * @return The byte size of the stored state
+ */
+static inline __attribute__((nonnull, nothrow, warn_unused_result, pure))
+size_t libkeccak_hmac_unmarshal_skip(const char* restrict data)
+{
+ size_t skip = libkeccak_state_unmarshal_skip(data);
+ data += skip / sizeof(char);
+ return skip + sizeof(size_t) + *(const size_t*)data + 2 * sizeof(char);
+}
+
+
+/**
+ * Absorb more, or the first part, of the message
+ * without wiping sensitive data when possible
+ *
+ * @param state The hashing state
+ * @param msg The partial message
+ * @param msglen The length of the partial message, in bytes
+ * @return Zero on success, -1 on error
+ */
+__attribute__((nonnull))
+int libkeccak_hmac_fast_update(libkeccak_hmac_state_t* restrict state, const char* restrict msg, size_t msglen);
+
+
+/**
+ * Absorb more, or the first part, of the message
+ * and wipe sensitive data when possible
+ *
+ * @param state The hashing state
+ * @param msg The partial message
+ * @param msglen The length of the partial message, in bytes
+ * @return Zero on success, -1 on error
+ */
+__attribute__((nonnull))
+int libkeccak_hmac_update(libkeccak_hmac_state_t* restrict state, const char* restrict msg, size_t msglen);
+
+
+/* TODO libkeccak_hmac_fast_digest(); */
+/* TODO libkeccak_hmac_digest(); */
+
+
+#endif
+
diff --git a/src/libkeccak/state.h b/src/libkeccak/state.h
index 33785a4..69a2455 100644
--- a/src/libkeccak/state.h
+++ b/src/libkeccak/state.h
@@ -30,7 +30,7 @@
/**
- * Satastructure that describes the state of a hashing process
+ * Datastructure that describes the state of a hashing process
*
* The `char`-size of the output hashsum is calculated by `(.n + 7) / 8`
*/
@@ -114,11 +114,10 @@ int libkeccak_state_initialise(libkeccak_state_t* restrict state, const libkecca
/**
* Reset a state according to hashing specifications
*
- * @param state The state that should be reset
- * @return Zero on success, -1 on error
+ * @param state The state that should be reset
*/
-__attribute__((nonnull, nothrow, unused))
-static inline void libkeccak_state_reset(libkeccak_state_t* restrict state)
+static inline __attribute__((nonnull, nothrow, unused))
+void libkeccak_state_reset(libkeccak_state_t* restrict state)
{
state->mptr = 0;
__builtin_memset(state->S, 0, sizeof(state->S));
@@ -184,8 +183,8 @@ void libkeccak_state_destroy(volatile libkeccak_state_t* restrict state)
/**
* Wrapper for `libkeccak_state_initialise` that also allocates the states
*
- * @param spec The specifications for the state
- * @return The state, `NULL` on error
+ * @param spec The specifications for the state
+ * @return The state, `NULL` on error
*/
static inline __attribute__((nonnull, unused, warn_unused_result))
libkeccak_state_t* libkeccak_state_create(const libkeccak_spec_t* restrict spec)
@@ -241,8 +240,8 @@ int libkeccak_state_copy(libkeccak_state_t* restrict dest, const libkeccak_state
/**
* A wrapper for `libkeccak_state_copy` that also allocates the duplicate
*
- * @param src The state to duplicate
- * @return The duplicate, `NULL` on error
+ * @param src The state to duplicate
+ * @return The duplicate, `NULL` on error
*/
static inline __attribute__((nonnull, unused, warn_unused_result))
libkeccak_state_t* libkeccak_state_duplicate(const libkeccak_state_t* restrict src)