aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Andrée <maandree@kth.se>2019-02-10 20:21:19 +0100
committerMattias Andrée <maandree@kth.se>2019-02-10 20:21:19 +0100
commited0296b9055713df0d910e4e7528ffe6fc539514 (patch)
tree8cbf8ecc9b6352257d6bc4946ff75cb8a4b484c0
downloadlibsha1-ed0296b9055713df0d910e4e7528ffe6fc539514.tar.gz
libsha1-ed0296b9055713df0d910e4e7528ffe6fc539514.tar.bz2
libsha1-ed0296b9055713df0d910e4e7528ffe6fc539514.tar.xz
First commit
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat (limited to '')
-rw-r--r--.gitignore12
-rw-r--r--LICENSE15
-rw-r--r--Makefile117
-rw-r--r--README12
-rw-r--r--algorithm_output_size.c21
-rw-r--r--behex_lower.c21
-rw-r--r--behex_upper.c21
-rw-r--r--common.h21
-rw-r--r--config.mk6
-rw-r--r--digest.c61
-rw-r--r--hmac_digest.c33
-rw-r--r--hmac_init.c53
-rw-r--r--hmac_marshal.c41
-rw-r--r--hmac_state_output_size.c15
-rw-r--r--hmac_unmarshal.c54
-rw-r--r--hmac_update.c23
-rw-r--r--init.c46
-rw-r--r--libsha1.h328
-rw-r--r--libsha1.h.0134
-rw-r--r--libsha1_algorithm_output_size.353
-rw-r--r--libsha1_behex_lower.349
-rw-r--r--libsha1_behex_upper.349
-rw-r--r--libsha1_digest.370
-rw-r--r--libsha1_hmac_digest.370
-rw-r--r--libsha1_hmac_init.366
-rw-r--r--libsha1_hmac_marshal.356
-rw-r--r--libsha1_hmac_state_output_size.340
-rw-r--r--libsha1_hmac_unmarshal.354
-rw-r--r--libsha1_hmac_update.342
-rw-r--r--libsha1_init.362
-rw-r--r--libsha1_marshal.356
-rw-r--r--libsha1_state_output_size.340
-rw-r--r--libsha1_sum_fd.373
-rw-r--r--libsha1_unhex.348
-rw-r--r--libsha1_unmarshal.354
-rw-r--r--libsha1_update.343
-rw-r--r--linux.mk5
-rw-r--r--macos.mk5
-rw-r--r--marshal.c43
-rw-r--r--process.c88
-rw-r--r--state_output_size.c15
-rw-r--r--sum_fd.c45
-rw-r--r--test.c309
-rw-r--r--unhex.c28
-rw-r--r--unmarshal.c59
-rw-r--r--update.c39
46 files changed, 2595 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..49bee63
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+*\#*
+*~
+*.bak
+*.swp
+*.swo
+*.out
+*.o
+*.so
+*.su
+*.a
+*.lo
+/test
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c437151
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+© 2019 Mattias Andrée <maandree@kth.se>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2f51528
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,117 @@
+.NONPOSIX:
+
+CONFIGFILE = config.mk
+
+OSCONFIGFILE = linux.mk
+# Change to macos.mk for Mac OS
+
+LIB_MAJOR = 1
+LIB_MINOR = 0
+LIB_VERSION = $(LIB_MAJOR).$(LIB_MINOR)
+
+include $(CONFIGFILE)
+include $(OSCONFIGFILE)
+
+HDR =\
+ libsha1.h\
+ common.h
+
+OBJ =\
+ algorithm_output_size.o\
+ behex_lower.o\
+ behex_upper.o\
+ digest.o\
+ hmac_digest.o\
+ hmac_init.o\
+ hmac_marshal.o\
+ hmac_state_output_size.o\
+ hmac_unmarshal.o\
+ hmac_update.o\
+ init.o\
+ marshal.o\
+ process.o\
+ state_output_size.o\
+ sum_fd.o\
+ unhex.o\
+ unmarshal.o\
+ update.o\
+
+MAN0 =\
+ libsha1.h.0
+
+MAN3 =\
+ libsha1_algorithm_output_size.3\
+ libsha1_behex_lower.3\
+ libsha1_behex_upper.3\
+ libsha1_digest.3\
+ libsha1_hmac_digest.3\
+ libsha1_hmac_init.3\
+ libsha1_hmac_marshal.3\
+ libsha1_hmac_state_output_size.3\
+ libsha1_hmac_unmarshal.3\
+ libsha1_hmac_update.3\
+ libsha1_init.3\
+ libsha1_marshal.3\
+ libsha1_state_output_size.3\
+ libsha1_sum_fd.3\
+ libsha1_unhex.3\
+ libsha1_unmarshal.3\
+ libsha1_update.3
+
+
+all: libsha1.a libsha1.$(LIBEXT) test
+$(OBJ): $(@:.o=.c) $(HDR)
+$(OBJ:.o=.lo): $(@:.lo=.c) $(HDR)
+
+.c.o:
+ $(CC) -c -o $@ $< $(CFLAGS)
+
+.c.lo:
+ $(CC) -fPIC -c -o $@ $< $(CFLAGS)
+
+test: test.o libsha1.a
+ $(CC) -o $@ test.o libsha1.a $(LDFLAGS)
+
+libsha1.$(LIBEXT): $(OBJ:.o=.lo)
+ $(CC) $(LIBFLAGS) -o $@ $(OBJ) $(LDFLAGS)
+
+libsha1.a: $(OBJ)
+ -rm -f -- $@
+ $(AR) rc $@ $(OBJ)
+ $(AR) -s $@
+
+check: test
+ ./test
+
+install:
+ mkdir -p -- "$(DESTDIR)$(PREFIX)/lib"
+ mkdir -p -- "$(DESTDIR)$(PREFIX)/include"
+ mkdir -p -- "$(DESTDIR)$(PREFIX)/share/licenses/libsha1"
+ mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man0"
+ mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man3"
+ cp -- libsha1.a "$(DESTDIR)$(PREFIX)/lib"
+ cp -- libsha1.$(LIBEXT) "$(DESTDIR)$(PREFIX)/lib/libsha1.$(LIBMINOREXT)"
+ ln -sf -- "libsha1.$(LIBMINOREXT).$(LIB_MINOR)" "$(DESTDIR)$(PREFIX)/lib/libsha1.$(LIBMAJOREXT)"
+ ln -sf -- "libsha1.$(LIBMAJOREXT)" "$(DESTDIR)$(PREFIX)/lib/libsha1.$(LIBEXT)"
+ cp -- libsha1.h "$(DESTDIR)$(PREFIX)/include"
+ cp -- $(MAN0) "$(DESTDIR)$(MANPREFIX)/man0"
+ cp -- $(MAN3) "$(DESTDIR)$(MANPREFIX)/man3"
+ cp -- LICENSE "$(DESTDIR)$(PREFIX)/share/licenses/libsha1"
+
+uninstall:
+ -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libsha1.a"
+ -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libsha1.$(LIBEXT)"
+ -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libsha1.$(LIBMAJOREXT)"
+ -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libsha1.$(LIBMINOREXT)"
+ -rm -f -- "$(DESTDIR)$(PREFIX)/include/libsha1.h"
+ -cd -- "$(DESTDIR)$(MANPREFIX)/man0" && rm -f -- $(MAN0)
+ -cd -- "$(DESTDIR)$(MANPREFIX)/man3" && rm -f -- $(MAN3)
+ -rm -rf -- "$(DESTDIR)$(PREFIX)/share/licenses/libsha1"
+
+clean:
+ -rm -f -- *.o *.lo *.su *.a *.so test
+
+.SUFFIXES:
+.SUFFIXES: .lo .o .c
+
+.PHONY: all check install uninstall clean
diff --git a/README b/README
new file mode 100644
index 0000000..ef1cc76
--- /dev/null
+++ b/README
@@ -0,0 +1,12 @@
+NAME
+ libsha1 - SHA-1 and SHA-0 hashing library
+
+DESCRIPTION
+ libsha1 is a bit-oriented implementation of the SHA-1
+ and SHA-0 algorithm
+
+RATIONALE
+ This should be obvious.
+
+SEE ALSO
+ libkeccak(7), libsha2(7), sha1sum(1)
diff --git a/algorithm_output_size.c b/algorithm_output_size.c
new file mode 100644
index 0000000..ffba6d8
--- /dev/null
+++ b/algorithm_output_size.c
@@ -0,0 +1,21 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+/**
+ * Get the output size of an algorithm
+ *
+ * @param algorithm The hashing algorithm
+ * @return The number of bytes in the output, zero on error
+ */
+size_t
+libsha1_algorithm_output_size(enum libsha1_algorithm algorithm)
+{
+ switch (algorithm) {
+ case LIBSHA1_0: return 20;
+ case LIBSHA1_1: return 20;
+ default:
+ errno = EINVAL;
+ return 0;
+ }
+}
diff --git a/behex_lower.c b/behex_lower.c
new file mode 100644
index 0000000..c69eaa0
--- /dev/null
+++ b/behex_lower.c
@@ -0,0 +1,21 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+/**
+ * Convert a binary hashsum to lower case hexadecimal representation
+ *
+ * @param output Output array, should have an allocation size of at least `2 * n + 1`
+ * @param hashsum The hashsum to convert
+ * @param n The size of `hashsum`
+ */
+void
+libsha1_behex_lower(char *restrict output, const void *restrict hashsum_, size_t n)
+{
+ const unsigned char *restrict hashsum = hashsum_;
+ output[2 * n] = '\0';
+ while (n--) {
+ output[2 * n + 0] = "0123456789abcdef"[(hashsum[n] >> 4) & 15];
+ output[2 * n + 1] = "0123456789abcdef"[(hashsum[n] >> 0) & 15];
+ }
+}
diff --git a/behex_upper.c b/behex_upper.c
new file mode 100644
index 0000000..19fa317
--- /dev/null
+++ b/behex_upper.c
@@ -0,0 +1,21 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+/**
+ * Convert a binary hashsum to upper case hexadecimal representation
+ *
+ * @param output Output array, should have an allocation size of at least `2 * n + 1`
+ * @param hashsum The hashsum to convert
+ * @param n The size of `hashsum`
+ */
+void
+libsha1_behex_upper(char *restrict output, const void *restrict hashsum_, size_t n)
+{
+ const unsigned char *restrict hashsum = hashsum_;
+ output[2 * n] = '\0';
+ while (n--) {
+ output[2 * n + 0] = "0123456789ABCDEF"[(hashsum[n] >> 4) & 15];
+ output[2 * n + 1] = "0123456789ABCDEF"[(hashsum[n] >> 0) & 15];
+ }
+}
diff --git a/common.h b/common.h
new file mode 100644
index 0000000..ad3be05
--- /dev/null
+++ b/common.h
@@ -0,0 +1,21 @@
+/* See LICENSE file for copyright and license details. */
+#include "libsha1.h"
+
+#include <sys/stat.h>
+#include <alloca.h>
+#include <errno.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+
+
+/**
+ * Process a chunk using SHA-1 or SHA-0
+ *
+ * @param state The hashing state
+ * @param chunk The data to process
+ */
+#if defined(__GNUC__)
+__attribute__((__nonnull__, __nothrow__))
+#endif
+void libsha1_process(struct libsha1_state *restrict, const unsigned char *restrict);
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..cd79dd2
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,6 @@
+PREFIX = /usr
+MANPREFIX = $(PREFIX)/share/man
+
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700
+CFLAGS = -std=c99 -Wall -Wextra -O3 $(CPPFLAGS)
+LDFLAGS = -s
diff --git a/digest.c b/digest.c
new file mode 100644
index 0000000..fd40e8f
--- /dev/null
+++ b/digest.c
@@ -0,0 +1,61 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+/**
+ * Absorb the last part of the message and output a hash
+ *
+ * @param state The hashing state
+ * @param message The message, in bits
+ * @param msglen The length of the message, zero if there is nothing more to absorb
+ * @param output The output buffer for the hash
+ */
+void
+libsha1_digest(struct libsha1_state *restrict state, const void *message_, size_t msglen, void *output_)
+{
+ const char *message = message_;
+ unsigned char *output = output_;
+ size_t off, i, n;
+
+ if (msglen & ~(size_t)7) {
+ libsha1_update(state, message, msglen & ~(size_t)7);
+ message += msglen & ~(size_t)7;
+ msglen &= (size_t)7;
+ }
+
+ off = (state->message_size / 8) % state->chunk_size;
+ if (msglen) {
+ state->chunk[off] = (unsigned char)(*message << (8 - (int)msglen));
+ state->chunk[off] |= (unsigned char)(1 << (7 - msglen));
+ state->chunk[off] &= (unsigned char)~((1 << (7 - msglen)) - 1);
+ state->message_size += msglen;
+ } else {
+ state->chunk[off] = 0x80;
+ }
+ off += 1;
+
+ if (off > state->chunk_size - (size_t)8) {
+ memset(state->chunk + off, 0, state->chunk_size - off);
+ off = 0;
+ libsha1_process(state, state->chunk);
+ }
+
+ memset(state->chunk + off, 0, state->chunk_size - 8 - off);
+ state->chunk[state->chunk_size - 8] = (unsigned char)(state->message_size >> 56);
+ state->chunk[state->chunk_size - 7] = (unsigned char)(state->message_size >> 48);
+ state->chunk[state->chunk_size - 6] = (unsigned char)(state->message_size >> 40);
+ state->chunk[state->chunk_size - 5] = (unsigned char)(state->message_size >> 32);
+ state->chunk[state->chunk_size - 4] = (unsigned char)(state->message_size >> 24);
+ state->chunk[state->chunk_size - 3] = (unsigned char)(state->message_size >> 16);
+ state->chunk[state->chunk_size - 2] = (unsigned char)(state->message_size >> 8);
+ state->chunk[state->chunk_size - 1] = (unsigned char)(state->message_size >> 0);
+ libsha1_process(state, state->chunk);
+
+ n = libsha1_algorithm_output_size(state->algorithm);
+ for (i = 0, n /= 4; i < n; i++) {
+ output[4 * i + 0] = (unsigned char)(state->h[i] >> 24);
+ output[4 * i + 1] = (unsigned char)(state->h[i] >> 16);
+ output[4 * i + 2] = (unsigned char)(state->h[i] >> 8);
+ output[4 * i + 3] = (unsigned char)(state->h[i] >> 0);
+ }
+}
diff --git a/hmac_digest.c b/hmac_digest.c
new file mode 100644
index 0000000..4acb1a1
--- /dev/null
+++ b/hmac_digest.c
@@ -0,0 +1,33 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+/**
+ * Feed data into the HMAC algorithm and
+ * get the result
+ *
+ * The state of the algorithm will be reset and
+ * `libsha1_hmac_update` and `libsha1_hmac_update`
+ * can be called again
+ *
+ * @param state The state of the algorithm
+ * @param data Data to feed into the algorithm
+ * @param n The number of bytes to feed into the algorithm
+ * @param output The output buffer for the hash, it will be as
+ * large as for the underlaying hash algorithm
+ */
+void
+libsha1_hmac_digest(struct libsha1_hmac_state *restrict state, const void *data, size_t n, void *output)
+{
+ if (!state->inited) {
+ libsha1_init(&state->sha1_state, state->sha1_state.algorithm);
+ libsha1_update(&state->sha1_state, state->ipad, state->sha1_state.chunk_size * 8);
+ }
+
+ libsha1_digest(&state->sha1_state, data, n, output);
+ libsha1_init(&state->sha1_state, state->sha1_state.algorithm);
+
+ libsha1_update(&state->sha1_state, state->opad, state->sha1_state.chunk_size * 8);
+ libsha1_digest(&state->sha1_state, output, state->outsize, output);
+ state->inited = 0;
+}
diff --git a/hmac_init.c b/hmac_init.c
new file mode 100644
index 0000000..c16998a
--- /dev/null
+++ b/hmac_init.c
@@ -0,0 +1,53 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+/**
+ * Initialise an HMAC state
+ *
+ * @param state The state that should be initialised
+ * @param algorithm The hashing algorithm
+ * @param key The key
+ * @param key_length The length of key, in bits
+ * @return Zero on success, -1 on error
+ */
+int
+libsha1_hmac_init(struct libsha1_hmac_state *restrict state, enum libsha1_algorithm algorithm,
+ const void *restrict key_, size_t keylen)
+{
+ const unsigned char *restrict key = key_;
+ size_t i;
+
+ state->sha1_state.algorithm = algorithm;
+ state->outsize = libsha1_algorithm_output_size(algorithm) * 8;
+ if (!state->outsize) {
+ errno = EINVAL;
+ return -1;
+ }
+ state->inited = 0;
+
+ if (keylen <= 64 * 8) {
+ memset(state->ipad, 0x36, sizeof(state->ipad));
+ memset(state->opad, 0x5C, sizeof(state->opad));
+ for (i = 0; i < keylen / 8; i++) {
+ state->ipad[i] ^= key[i];
+ state->opad[i] ^= key[i];
+ }
+ if (keylen & 7) {
+ state->ipad[i] ^= (unsigned char)(key[i] << (8 - (keylen & 7)));
+ state->opad[i] ^= (unsigned char)(key[i] << (8 - (keylen & 7)));
+ }
+ } else {
+ memset(state->ipad, 0, sizeof(state->ipad));
+ if (libsha1_init(&state->sha1_state, algorithm))
+ return -1;
+ libsha1_digest(&state->sha1_state, key, keylen, state->ipad);
+ memcpy(state->opad, state->ipad, sizeof(state->ipad));
+ for (i = 0; i < sizeof(state->ipad); i++) {
+ state->ipad[i] ^= 0x36;
+ state->opad[i] ^= 0x5C;
+ }
+ }
+
+ return 0;
+}
diff --git a/hmac_marshal.c b/hmac_marshal.c
new file mode 100644
index 0000000..70d030b
--- /dev/null
+++ b/hmac_marshal.c
@@ -0,0 +1,41 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+/**
+ * Marshal an HMAC state into a buffer
+ *
+ * @param state The state to marshal
+ * @param buf Output buffer, `NULL` to only return the required size
+ * @return The number of bytes marshalled to `buf`
+ */
+size_t
+libsha1_hmac_marshal(const struct libsha1_hmac_state *restrict state, void *restrict buf_)
+{
+ char *restrict buf = buf_;
+ size_t off = 0;
+
+ if (buf)
+ *(int *)buf = 0; /* version */
+ off += sizeof(int);
+
+ off += libsha1_marshal(&state->sha1_state, buf ? &buf[off] : NULL);
+
+ if (buf)
+ *(size_t *)&buf[off] = state->outsize;
+ off += sizeof(size_t);
+
+ if (buf)
+ *(unsigned char *)&buf[off] = state->inited;
+ off += sizeof(unsigned char);
+
+ if (buf)
+ memcpy(&buf[off], state->ipad, state->sha1_state.chunk_size);
+ off += state->sha1_state.chunk_size;
+
+ if (buf)
+ memcpy(&buf[off], state->opad, state->sha1_state.chunk_size);
+ off += state->sha1_state.chunk_size;
+
+ return off;
+}
diff --git a/hmac_state_output_size.c b/hmac_state_output_size.c
new file mode 100644
index 0000000..68e5ed2
--- /dev/null
+++ b/hmac_state_output_size.c
@@ -0,0 +1,15 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+/**
+ * Get the output size of the algorithm specified for a HMAC state
+ *
+ * @param state The state
+ * @return The number of bytes in the output, zero on error
+ */
+size_t
+libsha1_hmac_state_output_size(const struct libsha1_hmac_state *restrict state)
+{
+ return libsha1_algorithm_output_size(state->sha1_state.algorithm);
+}
diff --git a/hmac_unmarshal.c b/hmac_unmarshal.c
new file mode 100644
index 0000000..8db73c8
--- /dev/null
+++ b/hmac_unmarshal.c
@@ -0,0 +1,54 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+/**
+ * Unmarshal an HMAC state from a buffer
+ *
+ * @param state Output parameter for the unmarshalled state
+ * @param buf The buffer from which the state shall be unmarshalled
+ * @param bufsize The maximum number of bytes that can be unmarshalled
+ * @return The number of read bytes, 0 on failure
+ */
+size_t
+libsha1_hmac_unmarshal(struct libsha1_hmac_state *restrict state, const void *restrict buf_, size_t bufsize)
+{
+ const char *restrict buf = buf_;
+ size_t off = 0;
+ size_t r;
+
+ if (bufsize < sizeof(int)) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ if (*(const int *)buf) { /* version */
+ errno = EINVAL;
+ return 0;
+ }
+ off += sizeof(int);
+
+ r = libsha1_unmarshal(&state->sha1_state, &buf[off], bufsize - off);
+ if (!r)
+ return 0;
+ off += r;
+
+ if (bufsize - off < sizeof(size_t) + sizeof(unsigned char) + 2 * state->sha1_state.chunk_size) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ state->outsize = *(const size_t *)&buf[off];
+ off += sizeof(size_t);
+
+ state->inited = *(const unsigned char *)&buf[off];
+ off += sizeof(unsigned char);
+
+ memcpy(state->ipad, &buf[off], state->sha1_state.chunk_size);
+ off += state->sha1_state.chunk_size;
+
+ memcpy(state->opad, &buf[off], state->sha1_state.chunk_size);
+ off += state->sha1_state.chunk_size;
+
+ return off;
+}
diff --git a/hmac_update.c b/hmac_update.c
new file mode 100644
index 0000000..40528a4
--- /dev/null
+++ b/hmac_update.c
@@ -0,0 +1,23 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+/**
+ * Feed data into the HMAC algorithm
+ *
+ * @param state The state of the algorithm
+ * @param data Data to feed into the algorithm
+ * @param n The number of bytes to feed into the
+ * algorithm, this must be a multiple of 8
+ */
+void
+libsha1_hmac_update(struct libsha1_hmac_state *restrict state, const void *restrict data, size_t n)
+{
+ if (!state->inited) {
+ libsha1_init(&state->sha1_state, state->sha1_state.algorithm);
+ libsha1_update(&state->sha1_state, state->ipad, state->sha1_state.chunk_size * 8);
+ state->inited = 1;
+ }
+
+ libsha1_update(&state->sha1_state, data, n);
+}
diff --git a/init.c b/init.c
new file mode 100644
index 0000000..4e31556
--- /dev/null
+++ b/init.c
@@ -0,0 +1,46 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+/**
+ * Initial state for SHA-0
+ */
+static const uint32_t H_0[] = {
+ 0, 0, 0, 0, 0
+};
+
+/**
+ * Initial state for SHA_1
+ */
+static const uint32_t H_1[] = {
+ 0x67452301UL, 0xEFCDAB89UL, 0x98BADCFEUL, 0x10325476UL, 0xC3D2E1F0UL
+};
+
+
+/**
+ * Initialise a state
+ *
+ * @param state The state that should be initialised
+ * @param algorithm The hashing algorithm
+ * @return Zero on success, -1 on error
+ */
+int
+libsha1_init(struct libsha1_state *restrict state, enum libsha1_algorithm algorithm)
+{
+ memset(state, 0, sizeof(*state));
+ state->message_size = 0;
+ state->algorithm = algorithm;
+
+ /* Set initial hash values. */
+ switch (algorithm) {
+ case LIBSHA1_0: memcpy(state->h, H_0, sizeof(H_0)); break;
+ case LIBSHA1_1: memcpy(state->h, H_1, sizeof(H_1)); break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ state->chunk_size = 64;
+
+ return 0;
+}
diff --git a/libsha1.h b/libsha1.h
new file mode 100644
index 0000000..a263ea3
--- /dev/null
+++ b/libsha1.h
@@ -0,0 +1,328 @@
+/* See LICENSE file for copyright and license details. */
+#ifndef LIBSHA1_H
+#define LIBSHA1_H 1
+
+#include <stdint.h>
+#include <stddef.h>
+
+
+/**
+ * Algorithms supported by libsha1
+ */
+enum libsha1_algorithm {
+ /**
+ * SHA-0, outputs 20 bytes
+ */
+ LIBSHA1_0,
+
+ /**
+ * SHA-1, outputs 20 bytes
+ */
+ LIBSHA1_1
+};
+
+/**
+ * Data structure that describes the state of a hashing process
+ *
+ * Data that could just as well be allocated (with `auto`) are
+ * allocated here so that is is easier to wipe the data without
+ * exposing two versions of each function: one to wipe data,
+ * and one not to wipe data to gain speed, now you can use use
+ * `explicit_bzero` (or `memset`) when you are done.
+ */
+struct libsha1_state {
+ /**
+ * The size of the message, as far as processed, in bits;
+ */
+ size_t message_size;
+
+ /**
+ * Words
+ *
+ * Does not need to be marshalled
+ */
+ uint32_t w[80];
+
+ /**
+ * Hashing values
+ */
+ uint32_t h[5];
+
+ /**
+ * Space for chunks to process
+ */
+ unsigned char chunk[64];
+
+ /**
+ * The size of the chunks, in bytes
+ */
+ size_t chunk_size;
+
+ /**
+ * The algorithm that is used
+ */
+ enum libsha1_algorithm algorithm;
+
+ int __padding1;
+};
+
+
+/**
+ * Data structure that describes the state of a HMAC hashing process
+ *
+ * Data that could just as well be allocated (with `auto`) are
+ * allocated here so that is is easier to wipe the data without
+ * exposing two versions of each function: one to wipe data,
+ * and one not to wipe data to gain speed, now you can use use
+ * `explicit_bzero` (or `memset`) when you are done.
+ */
+struct libsha1_hmac_state {
+ /**
+ * State of the underlaying hash function
+ */
+ struct libsha1_state sha1_state;
+
+ /**
+ * The output size of the underlaying
+ * hash algorithm, in bits
+ */
+ size_t outsize;
+
+ /**
+ * Whether `.sha1_state` has been initialised
+ * and whether the `ipad` has been feed into
+ * the algorithm
+ */
+ unsigned char inited;
+
+ /**
+ * Inner pad XOR processed key
+ */
+ unsigned char ipad[128];
+
+ /**
+ * Outer pad XOR processed key
+ */
+ unsigned char opad[128];
+};
+
+
+/**
+ * Initialise a state
+ *
+ * @param state The state that should be initialised
+ * @param algorithm The hashing algorithm
+ * @return Zero on success, -1 on error
+ */
+#if defined(__GNUC__)
+__attribute__((__leaf__, __nothrow__, __nonnull__))
+#endif
+int libsha1_init(struct libsha1_state *restrict, enum libsha1_algorithm);
+
+/**
+ * Get the output size of the algorithm specified for a state
+ *
+ * @param state The state
+ * @return The number of bytes in the output, zero on error
+ */
+#if defined(__GNUC__)
+__attribute__((__nothrow__, __nonnull__, __pure__))
+#endif
+size_t libsha1_state_output_size(const struct libsha1_state *restrict);
+
+/**
+ * Get the output size of an algorithm
+ *
+ * @param algorithm The hashing algorithm
+ * @return The number of bytes in the output, zero on error
+ */
+#if defined(__GNUC__)
+__attribute__((__leaf__, __nothrow__, __const__))
+#endif
+size_t libsha1_algorithm_output_size(enum libsha1_algorithm);
+
+/**
+ * Absorb more of the message
+ *
+ * @param state The hashing state
+ * @param message The message, in bits, must be equivalent to 0 modulus 8
+ * @param msglen The length of the message
+ */
+#if defined(__GNUC__)
+__attribute__((__nonnull__, __nothrow__))
+#endif
+void libsha1_update(struct libsha1_state *restrict, const void *restrict, size_t);
+
+/**
+ * Absorb the last part of the message and output a hash
+ *
+ * @param state The hashing state
+ * @param message The message, in bits
+ * @param msglen The length of the message, zero if there is nothing more to absorb
+ * @param output The output buffer for the hash
+ */
+#if defined(__GNUC__)
+__attribute__((__nonnull__(1, 4), __nothrow__))
+#endif
+void libsha1_digest(struct libsha1_state *restrict, const void *, size_t, void *);
+
+/**
+ * Calculate the checksum for a file,
+ * the content of the file is assumed non-sensitive
+ *
+ * @param fd The file descriptor of the file
+ * @param algorithm The hashing algorithm
+ * @param hashsum Output buffer for the hash
+ * @return Zero on success, -1 on error
+ */
+#if defined(__GNUC__)
+__attribute__((__nonnull__, __leaf__))
+#endif
+int libsha1_sum_fd(int, enum libsha1_algorithm, void *restrict);
+
+/**
+ * Convert a binary hashsum to lower case hexadecimal representation
+ *
+ * @param output Output array, should have an allocation size of at least `2 * n + 1`
+ * @param hashsum The hashsum to convert
+ * @param n The size of `hashsum`
+ */
+#if defined(__GNUC__)
+__attribute__((__leaf__, __nonnull__, __nothrow__))
+#endif
+void libsha1_behex_lower(char *restrict, const void *restrict, size_t);
+
+/**
+ * Convert a binary hashsum to upper case hexadecimal representation
+ *
+ * @param output Output array, should have an allocation size of at least `2 * n + 1`
+ * @param hashsum The hashsum to convert
+ * @param n The size of `hashsum`
+ */
+#if defined(__GNUC__)
+__attribute__((__leaf__, __nonnull__, __nothrow__))
+#endif
+void libsha1_behex_upper(char *restrict, const void *restrict, size_t);
+
+/**
+ * Convert a hexadecimal hashsum (both lower case, upper
+ * case and mixed is supported) to binary representation
+ *
+ * @param output Output array, should have an allocation
+ * size of at least `strlen(hashsum) / 2`
+ * @param hashsum The hashsum to convert
+ */
+#if defined(__GNUC__)
+__attribute__((__leaf__, __nonnull__, __nothrow__))
+#endif
+void libsha1_unhex(void *restrict, const char *restrict);
+
+/**
+ * Marshal a state into a buffer
+ *
+ * @param state The state to marshal
+ * @param buf Output buffer, `NULL` to only return the required size
+ * @return The number of bytes marshalled to `buf`
+ */
+#if defined(__GNUC__)
+__attribute__((__leaf__, __nonnull__(1), __nothrow__))
+#endif
+size_t libsha1_marshal(const struct libsha1_state *restrict, void *restrict);
+
+/**
+ * Unmarshal a state from a buffer
+ *
+ * @param state Output parameter for the unmarshalled state
+ * @param buf The buffer from which the state shall be unmarshalled
+ * @param bufsize The maximum number of bytes that can be unmarshalled
+ * @return The number of read bytes, 0 on failure
+ */
+#if defined(__GNUC__)
+__attribute__((__leaf__, __nonnull__, __nothrow__))
+#endif
+size_t libsha1_unmarshal(struct libsha1_state *restrict, const void *restrict, size_t);
+
+/**
+ * Initialise an HMAC state
+ *
+ * @param state The state that should be initialised
+ * @param algorithm The hashing algorithm
+ * @param key The key
+ * @param key_length The length of key, in bits
+ * @return Zero on success, -1 on error
+ */
+#if defined(__GNUC__)
+__attribute__((__leaf__, __nonnull__, __nothrow__))
+#endif
+int libsha1_hmac_init(struct libsha1_hmac_state *restrict, enum libsha1_algorithm, const void *restrict, size_t);
+
+/**
+ * Get the output size of the algorithm specified for an HMAC state
+ *
+ * @param state The state
+ * @return The number of bytes in the output, zero on error
+ */
+#if defined(__GNUC__)
+__attribute__((__nothrow__, __nonnull__, __pure__))
+#endif
+size_t libsha1_hmac_state_output_size(const struct libsha1_hmac_state *restrict);
+
+/**
+ * Feed data into the HMAC algorithm
+ *
+ * @param state The state of the algorithm
+ * @param data Data to feed into the algorithm
+ * @param n The number of bytes to feed into the
+ * algorithm, this must be a multiple of 8
+ */
+#if defined(__GNUC__)
+__attribute__((__leaf__, __nonnull__, __nothrow__))
+#endif
+void libsha1_hmac_update(struct libsha1_hmac_state *restrict, const void *restrict, size_t);
+
+/**
+ * Feed data into the HMAC algorithm and
+ * get the result
+ *
+ * The state of the algorithm will be reset and
+ * `libsha1_hmac_update` and `libsha1_hmac_update`
+ * can be called again
+ *
+ * @param state The state of the algorithm
+ * @param data Data to feed into the algorithm
+ * @param n The number of bytes to feed into the algorithm
+ * @param output The output buffer for the hash, it will be as
+ * large as for the underlaying hash algorithm
+ */
+#if defined(__GNUC__)
+__attribute__((__leaf__, __nonnull__, __nothrow__))
+#endif
+void libsha1_hmac_digest(struct libsha1_hmac_state *restrict, const void *, size_t, void *);
+
+/**
+ * Marshal an HMAC state into a buffer
+ *
+ * @param state The state to marshal
+ * @param buf Output buffer, `NULL` to only return the required size
+ * @return The number of bytes marshalled to `buf`
+ */
+#if defined(__GNUC__)
+__attribute__((__leaf__, __nonnull__(1), __nothrow__))
+#endif
+size_t libsha1_hmac_marshal(const struct libsha1_hmac_state *restrict, void *restrict);
+
+/**
+ * Unmarshal an HMAC state from a buffer
+ *
+ * @param state Output parameter for the unmarshalled state
+ * @param buf The buffer from which the state shall be unmarshalled
+ * @param bufsize The maximum number of bytes that can be unmarshalled
+ * @return The number of read bytes, 0 on failure
+ */
+#if defined(__GNUC__)
+__attribute__((__leaf__, __nonnull__, __nothrow__))
+#endif
+size_t libsha1_hmac_unmarshal(struct libsha1_hmac_state *restrict, const void *restrict, size_t);
+
+
+#endif
diff --git a/libsha1.h.0 b/libsha1.h.0
new file mode 100644
index 0000000..30d624b
--- /dev/null
+++ b/libsha1.h.0
@@ -0,0 +1,134 @@
+.TH LIBSHA1.H 0 2019-02-10 libsha1
+.SH NAME
+libsha1.h \- SHA 1 and SHA 0 library header
+.SH SYNOPSIS
+.nf
+#include <libsha1.h>
+
+enum libsha1_algorithm {
+ LIBSHA1_0, /* SHA-0 */
+ LIBSHA1_1 /* SHA-1 */
+};
+
+struct libsha1_state {
+ /* members omitted */
+};
+
+int libsha1_init(struct libsha1_state *restrict \fIstate\fP, enum libsha1_algorithm \fIalgorithm\fP);
+size_t libsha1_state_output_size(const struct libsha1_state *restrict \fIstate\fP);
+size_t libsha1_algorithm_output_size(enum libsha1_algorithm \fIalgorithm\fP);
+void libsha1_update(struct libsha1_state *restrict \fIstate\fP, const void *restrict \fImessage\fP, size_t \fImsglen\fP);
+void libsha1_digest(struct libsha1_state *restrict \fIstate\fP, const void *restrict \fImessage\fP, size_t \fImsglen\fP, void *\fIoutput\fP);
+int libsha1_sum_fd(int \fIfd\fP, enum libsha1_algorithm \fIalgorithm\fP, void *restrict \fIhashsum\fP);
+void libsha1_behex_lower(char *restrict \fIoutput\fP, const void *restrict \fIhashsum\fP, size_t \fIn\fP);
+void libsha1_behex_upper(char *restrict \fIoutput\fP, const void *restrict \fIhashsum\fP, size_t \fIn\fP);
+void libsha1_unhex(void *restrict \fIoutput\fP, const char *restrict \fIhashsum\fP);
+size_t libsha1_marshal(const struct libsha1_state *restrict \fIstate\fP, void *restrict \fIbuf\fP);
+size_t libsha1_unmarshal(struct libsha1_state *restrict \fIstate\fP, const void *restrict \fIbuf\fP, size_t \fIbufsize\fP);
+int libsha1_hmac_init(struct libsha1_hmac_state *restrict \fIstate\fP, enum libsha1_algorithm \fIalgorithm\fP,
+ const void *restrict \fIkey\fP, size_t \fIkeylen\fP);
+size_t libsha1_hmac_state_output_size(const struct libsha1_hmac_state *restrict \fIstate\fP);
+void libsha1_hmac_update(struct libsha1_hmac_state *restrict \fIstate\fP, const void *restrict \fIdata\fP, size_t \fIn\fP);
+void libsha1_hmac_digest(struct libsha1_hmac_state *restrict \fIstate\fP, const void *\fIdata\fP, size_t \fIn\fP, void *\fIoutput\fP);
+size_t libsha1_hmac_marshal(const struct libsha1_hmac_state *restrict \fIstate\fP, void *restrict \fIbuf\fP);
+size_t libsha1_hmac_unmarshal(struct libsha1_hmac_state *restrict \fIstate\fP, const void *restrict \fIbuf\fP, size_t \fIbufsize\fP);
+.fi
+.PP
+Link with
+.IR \-lsha1 .
+.SH DESCRIPTION
+The
+.B libsha1.h
+header, the header for the libsha1 C library defines
+.B enum libsha1_algorithm
+which has one value per supported algorithm:
+.TP
+.B LIBSHA1_0
+SHA-0
+.TP
+.B LIBSHA1_1
+SHA-1
+.PP
+Further, the
+.B libsha1.h
+header defines the opaque, but complete,
+.B struct libsha1_state
+which stores the selected algorithm and
+the state of the hashing. A state can be
+securely erased by overriding all bytes
+in the structure with zeroes (or any other
+byte sequence). The header also
+defines the functions:
+.TP
+.BR libsha1_init (3)
+Initialise hashing state.
+.TP
+.BR libsha1_state_output_size "(3), " libsha1_algorithm_output_size (3)
+Get the output size for an algorithm.
+.TP
+.BR libsha1_update (3)
+Feed data into the hashing state.
+.TP
+.BR libsha1_digest (3)
+Get the result of a hashing.
+.TP
+.BR libsha1_sum_fd (3)
+Hash an entire file.
+.TP
+.BR libsha1_behex_lower "(3), " libsha1_behex_upper (3)
+Convert binary output from
+.BR libsha1_digest (3)
+to hexadecimal.
+.TP
+.BR libsha1_unhex (3)
+Convert a hexadecimal hash to binary.
+.TP
+.BR libsha1_marshal (3)
+Marshal a hashing state.
+.TP
+.BR libsha1_unmarshal (3)
+Unmarshal a hashing state.
+.TP
+.BR libsha1_hmac_init (3)
+Initialise HMAC hashing state.
+.TP
+.BR libsha1_hmac_update (3)
+Feed data into the HMAC hashing state.
+.TP
+.BR libsha1_hmac_digest (3)
+Get the result of an HMAC hashing.
+.TP
+.BR libsha1_hmac_marshal (3)
+Marshal an HMAC hashing state.
+.TP
+.BR libsha1_hmac_unmarshal (3)
+Unmarshal an HMAC hashing state.
+.SH EXAMPLES
+None.
+.SH APPLICATION USAGE
+None.
+.SH RATIONALE
+None.
+.SH FUTURE DIRECTIONS
+None.
+.SH NOTES
+None.
+.SH BUGS
+None.
+.SH SEE ALSO
+.BR libsha1_algorithm_output_size (3),
+.BR libsha1_behex_lower (3),
+.BR libsha1_behex_upper (3),
+.BR libsha1_digest (3),
+.BR libsha1_hmac_digest (3),
+.BR libsha1_hmac_init (3),
+.BR libsha1_hmac_marshal (3),
+.BR libsha1_hmac_unmarshal (3),
+.BR libsha1_hmac_update (3),
+.BR libsha1_init (3),
+.BR libsha1_marshal (3),
+.BR libsha1_state_output_size (3),
+.BR libsha1_sum_fd (3),
+.BR libsha1_unhex (3),
+.BR libsha1_unmarshal (3),
+.BR libsha1_update (3)
diff --git a/libsha1_algorithm_output_size.3 b/libsha1_algorithm_output_size.3
new file mode 100644
index 0000000..f88d5f2
--- /dev/null
+++ b/libsha1_algorithm_output_size.3
@@ -0,0 +1,53 @@
+.TH LIBSHA1_ALGORITHM_OUTPUT_SIZE 3 2019-02-10 libsha1
+.SH NAME
+libsha1_algorithm_output_size \- Get the size of the output for a SHA 1/0 algorithm
+.SH SYNOPSIS
+.nf
+#include <libsha1.h>
+
+size_t libsha1_algorithm_output_size(enum libsha1_algorithm \fIalgorithm\fP);
+.fi
+.PP
+Link with
+.IR \-lsha1 .
+.SH DESCRIPTION
+The
+.BR libsha1_algorithm_output_size ()
+function get the output size for the
+binary output of the hash algorithm
+selected for the selected
+.IR algorithm .
+.SH RETURN VALUE
+The
+.BR libsha1_algorithm_output_size ()
+function returns the output size in bytes,
+a positive number upon successful completion,
+otherwise 0 is returned and
+.I errno
+is set appropriately.
+.SH ERRORS
+The
+.BR libsha1_algorithm_output_size ()
+function will fail if:
+.TP
+.B EINVAL
+.I algorithm
+is not a valid
+.B enum libsha1_algorithm
+value.
+.SH EXAMPLES
+None.
+.SH APPLICATION USAGE
+None.
+.SH RATIONALE
+None.
+.SH FUTURE DIRECTIONS
+None.
+.SH NOTES
+None.
+.SH BUGS
+Both GCC and Clang optimises out setting
+.I errno
+if using too high optimisation.
+.SH SEE ALSO
+.BR libsha1_state_output_size (3)
diff --git a/libsha1_behex_lower.3 b/libsha1_behex_lower.3
new file mode 100644
index 0000000..1d365c9
--- /dev/null
+++ b/libsha1_behex_lower.3
@@ -0,0 +1,49 @@
+.TH LIBSHA1_BEHEX_LOWER 3 2019-02-10 libsha1
+.SH NAME
+libsha1_behex_lower \- Convert binary to lower case hexadecimal
+.SH SYNOPSIS
+.nf
+#include <libsha1.h>
+
+void libsha1_behex_lower(char *restrict \fIoutput\fP, const void *restrict \fIhashsum\fP, size_t \fIn\fP);
+.fi
+.PP
+Link with
+.IR \-lsha1 .
+.SH DESCRIPTION
+The
+.BR libsha1_behex_lower ()
+function converts the
+.I n
+first bytes of
+.I hashsum
+to lower case hexadecimal, as exactly
+.I 2*n
+characters, and stores the result, with
+NUL byte termination, to
+.IR output .
+.PP
+The user must make sure that
+.I output
+is at least
+.I 2*n+1
+bytes large.
+.SH RETURN VALUE
+None.
+.SH ERRORS
+None.
+.SH EXAMPLES
+None.
+.SH APPLICATION USAGE
+None.
+.SH RATIONALE
+None.
+.SH FUTURE DIRECTIONS
+None.
+.SH NOTES
+None.
+.SH BUGS
+None.
+.SH SEE ALSO
+.BR libsha1_behex_upper (3),
+.BR libsha1_unhex (3)
diff --git a/libsha1_behex_upper.3 b/libsha1_behex_upper.3
new file mode 100644
index 0000000..82c0190
--- /dev/null
+++ b/libsha1_behex_upper.3
@@ -0,0 +1,49 @@
+.TH LIBSHA1_BEHEX_UPPER 3 2019-02-10 libsha1
+.SH NAME
+libsha1_behex_upper \- Convert binary to upper case hexadecimal
+.SH SYNOPSIS
+.nf
+#include <libsha1.h>
+
+void libsha1_behex_upper(char *restrict \fIoutput\fP, const void *restrict \fIhashsum\fP, size_t \fIn\fP);
+.fi
+.PP
+Link with
+.IR \-lsha1 .
+.SH DESCRIPTION
+The
+.BR libsha1_behex_upper ()
+function converts the
+.I n
+first bytes of
+.I hashsum
+to upper case hexadecimal, as exactly
+.I 2*n
+characters, and stores the result, with
+NUL byte termination, to
+.IR output .
+.PP
+The user must make sure that
+.I output
+is at least
+.I 2*n+1
+bytes large.
+.SH RETURN VALUE
+None.
+.SH ERRORS
+None.
+.SH EXAMPLES
+None.
+.SH APPLICATION USAGE
+None.
+.SH RATIONALE
+None.
+.SH FUTURE DIRECTIONS
+None.
+.SH NOTES
+None.
+.SH BUGS
+None.
+.SH SEE ALSO
+.BR libsha1_behex_lower (3),
+.BR libsha1_unhex (3)
diff --git a/libsha1_digest.3 b/libsha1_digest.3
new file mode 100644
index 0000000..6a1e62d
--- /dev/null
+++ b/libsha1_digest.3
@@ -0,0 +1,70 @@
+.TH LIBSHA1_DIGEST 3 2019-02-10 libsha1
+.SH NAME
+libsha1_digest \- Get the result of a SHA 1/0 hashing
+.SH SYNOPSIS
+.nf
+#include <libsha1.h>
+
+void libsha1_digest(struct libsha1_state *restrict \fIstate\fP, const void *\fImessage\fP, size_t \fImsglen\fP, void *\fIoutput\fP);
+.fi
+.PP
+Link with
+.IR \-lsha1 .
+.SH DESCRIPTION
+The
+.BR libsha1_digest ()
+function feeds the first
+.I msglen
+.B bits
+of
+.I message
+into the hashing state of the
+.I state
+parameter, and finalises the hashing.
+The resulting hash is stored in binary
+format in
+.IR output .
+The user must make sure that
+.I output
+is sufficiently large, which means at
+least the return value of the
+.BR libsha1_state_output_size (3)
+function.
+.PP
+If
+.I msglen
+is not a multiple of 8, the lowest
+.I msglen%8
+bits from the last by in
+.I message
+is used as the complete byte.
+.PP
+The
+.BR libsha1_behex_lower (3)
+and
+.BR libsha1_behex_upper (3)
+functions can be used to convert the
+result to hexadecimal format.
+.SH RETURN VALUE
+None.
+.SH ERRORS
+None.
+.SH EXAMPLES
+None.
+.SH APPLICATION USAGE
+None.
+.SH RATIONALE
+None.
+.SH FUTURE DIRECTIONS
+None.
+.SH NOTES
+None.
+.SH BUGS
+None.
+.SH SEE ALSO
+.BR libsha1_behex_lower (3),
+.BR libsha1_behex_upper (3),
+.BR libsha1_init (3),
+.BR libsha1_state_output_size (3),
+.BR libsha1_sum_fd (3),
+.BR libsha1_update (3)
diff --git a/libsha1_hmac_digest.3 b/libsha1_hmac_digest.3
new file mode 100644
index 0000000..10478ab
--- /dev/null
+++ b/libsha1_hmac_digest.3
@@ -0,0 +1,70 @@
+.TH LIBSHA1_HMAC_DIGEST 3 2019-02-10 libsha1
+.SH NAME
+libsha1_hmac_digest \- Get the result of a HMAC-SHA 1/0 hashing
+.SH SYNOPSIS
+.nf
+#include <libsha1.h>
+
+void libsha1_hmac_digest(struct libsha1_hmac_state *restrict \fIstate\fP, const void *\fImessage\fP, size_t \fImsglen\fP, void *\fIoutput\fP);
+.fi
+.PP
+Link with
+.IR \-lsha1 .
+.SH DESCRIPTION
+The
+.BR libsha1_hmac_digest ()
+function feeds the first
+.I msglen
+.B bits
+of
+.I message
+into the hashing state of the
+.I state
+parameter, and finalises the hashing.
+The resulting hash is stored in binary
+format in
+.IR output .
+The user must make sure that
+.I output
+is sufficiently large, which means at
+least the return value of the
+.BR libsha1_hmac_state_output_size (3)
+function.
+.PP
+If
+.I msglen
+is not a multiple of 8, the lowest
+.I msglen%8
+bits from the last by in
+.I message
+is used as the complete byte.
+.PP
+The
+.BR libsha1_behex_lower (3)
+and
+.BR libsha1_behex_upper (3)
+functions can be used to convert the
+result to hexadecimal format.
+.SH RETURN VALUE
+None.
+.SH ERRORS
+None.
+.SH EXAMPLES
+None.
+.SH APPLICATION USAGE
+None.
+.SH RATIONALE
+None.
+.SH FUTURE DIRECTIONS
+None.
+.SH NOTES
+None.
+.SH BUGS
+None.
+.SH SEE ALSO
+.BR libsha1_behex_lower (3),
+.BR libsha1_behex_upper (3),
+.BR libsha1_hmac_init (3),
+.BR libsha1_hmac_state_output_size (3),
+.BR libsha1_hmac_sum_fd (3),
+.BR libsha1_hmac_update (3)
diff --git a/libsha1_hmac_init.3 b/libsha1_hmac_init.3
new file mode 100644
index 0000000..b1e2617
--- /dev/null
+++ b/libsha1_hmac_init.3
@@ -0,0 +1,66 @@
+.TH LIBSHA1_HMAC_INIT 3 2019-02-10 libsha1
+.SH NAME
+libsha1_hmac_init \- Initialises hashing with an HMAC-SHA 1/0 algorithm
+.SH SYNOPSIS
+.nf
+#include <libsha1.h>
+
+enum libsha1_algorithm {
+ LIBSHA1_0, /* SHA-0 */
+ LIBSHA1_1 /* SHA-1 */
+};
+
+int libsha1_hmac_init(struct libsha1_hmac_state *restrict \fIstate\fP, enum libsha1_algorithm \fIalgorithm\fP,
+ const void *restrict \fIkey\fP, size_t \fIkeylen\fP);
+.fi
+.PP
+Link with
+.IR \-lsha1 .
+.SH DESCRIPTION
+The
+.BR libsha1_hmac_init ()
+function stores the selected
+.I algorithm
+in
+.I state
+and initialises
+.I state
+with the first
+.I keylen
+bits of
+.I key
+as the key.
+.SH RETURN VALUE
+The
+.BR libsha1_hmac_init ()
+function returns 0 upon successful completion,
+otherwise -1 is returned and
+.I errno
+is set appropriately.
+.SH ERRORS
+The
+.BR libsha1_hmac_init ()
+function will fail if:
+.TP
+.B EINVAL
+.I algorithm
+is not a valid
+.B enum libsha1_algorithm
+value.
+.SH EXAMPLES
+None.
+.SH APPLICATION USAGE
+None.
+.SH RATIONALE
+None.
+.SH FUTURE DIRECTIONS
+None.
+.SH NOTES
+None.
+.SH BUGS
+None.
+.SH SEE ALSO
+.BR libsha1_hmac_digest (3),
+.BR libsha1_hmac_marshal (3),
+.BR libsha1_hmac_unmarshal (3),
+.BR libsha1_hmac_update (3)
diff --git a/libsha1_hmac_marshal.3 b/libsha1_hmac_marshal.3
new file mode 100644
index 0000000..b8f1505
--- /dev/null
+++ b/libsha1_hmac_marshal.3
@@ -0,0 +1,56 @@
+.TH LIBSHA1_HMAC_MARSHAL 3 2019-02-10 libsha1
+.SH NAME
+libsha1_hmac_marshal \- Marshal an HMAC-SHA 1/0 hashing state
+.SH SYNOPSIS
+.nf
+#include <libsha1.h>
+
+size_t libsha1_hmac_marshal(const struct libsha1_hmac_state *restrict \fIstate\fP, void *restrict \fIbuf\fP);
+.fi
+.PP
+Link with
+.IR \-lsha1 .
+.SH DESCRIPTION
+The
+.BR libsha1_marshal ()
+function marshal
+.I state
+into the buffer
+.IR buf .
+If the function is called with
+.I NULL
+as
+.IR buf ,
+the required size for
+.I buf
+is returned.
+.PP
+A version number is marshalled into
+.IR buf ,
+this allows new versions of the library to
+unmarshal states marshalled by older versions.
+.SH RETURN VALUE
+The
+.BR libsha1_hmac_marshal ()
+function returns the number of marshalled
+bytes (or if
+.I buf
+is
+.IR NULL ,
+the number of bytes that would have been marshalled).
+.SH ERRORS
+None.
+.SH EXAMPLES
+None.
+.SH APPLICATION USAGE
+None.
+.SH RATIONALE
+None.
+.SH FUTURE DIRECTIONS
+None.
+.SH NOTES
+None.
+.SH BUGS
+None.
+.SH SEE ALSO
+.BR libsha1_hmac_unmarshal (3)
diff --git a/libsha1_hmac_state_output_size.3 b/libsha1_hmac_state_output_size.3
new file mode 100644
index 0000000..74a76fe
--- /dev/null
+++ b/libsha1_hmac_state_output_size.3
@@ -0,0 +1,40 @@
+.TH LIBSHA1_HMAC_STATE_OUTPUT_SIZE 3 2019-02-10 libsha1
+.SH NAME
+libsha1_hmac_state_output_size \- Get the size of the output for a HMAC-SHA 1/0 algorithm
+.SH SYNOPSIS
+.nf
+#include <libsha1.h>
+
+size_t libsha1_hmac_state_output_size(const struct libsha1_hmac_state *restrict \fIstate\fP);
+.fi
+.PP
+Link with
+.IR \-lsha1 .
+.SH DESCRIPTION
+The
+.BR libsha1_hmac_state_output_size ()
+function get the output size for the
+binary output of the hash algorithm
+selected for
+.IR state .
+.SH RETURN VALUE
+The
+.BR libsha1_hmac_state_output_size ()
+function returns the output size in bytes,
+a positive number.
+.SH ERRORS
+None.
+.SH EXAMPLES
+None.
+.SH APPLICATION USAGE
+None.
+.SH RATIONALE
+None.
+.SH FUTURE DIRECTIONS
+None.
+.SH NOTES
+None.
+.SH BUGS
+None.
+.SH SEE ALSO
+None.
diff --git a/libsha1_hmac_unmarshal.3 b/libsha1_hmac_unmarshal.3
new file mode 100644
index 0000000..a21e462
--- /dev/null
+++ b/libsha1_hmac_unmarshal.3
@@ -0,0 +1,54 @@
+.TH LIBSHA1_HMAC_UNMARSHAL 3 2019-02-10 libsha1
+.SH NAME
+libsha1_hmac_unmarshal \- Unmarshal an HMAC-SHA 1/0 hashing state
+.SH SYNOPSIS
+.nf
+#include <libsha1.h>
+
+size_t libsha1_hmac_unmarshal(struct libsha1_hmac_state *restrict \fIstate\fP, const void *restrict \fIbuf\fP, size_t \fIbufsize\fP);
+.fi
+.PP
+Link with
+.IR \-lsha1 .
+.SH DESCRIPTION
+The
+.BR libsha1_hmac_unmarshal ()
+function unmarshal
+.I state
+from the buffer
+.IR buf .
+.I bufsize
+shall be the maximum number of bytes the
+function may read from
+.IR buf .
+.SH RETURN VALUE
+The
+.BR libsha1_hmac_unmarshal ()
+function returns the number of unmarshalled
+bytes (this number is always positive) upon
+successful completion, otherwise 0 is returned.
+.SH ERRORS
+The
+.BR libsha1_hmac_unmarshal ()
+function will fail if:
+.TP
+.B EINVAL
+.I bufsize
+is too small or the contents of
+.I buf
+is invalid or created with an incompatible
+version of the library.
+.SH EXAMPLES
+None.
+.SH APPLICATION USAGE
+None.
+.SH RATIONALE
+None.
+.SH FUTURE DIRECTIONS
+None.
+.SH NOTES
+None.
+.SH BUGS
+None.
+.SH SEE ALSO
+.BR libsha1_hmac_marshal (3)
diff --git a/libsha1_hmac_update.3 b/libsha1_hmac_update.3
new file mode 100644
index 0000000..caff71c
--- /dev/null
+++ b/libsha1_hmac_update.3
@@ -0,0 +1,42 @@
+.TH LIBSHA1_HMAC_UPDATE 3 2019-02-10 libsha1
+.SH NAME
+libsha1_hmac_update \- Feed data into a HMAC-SHA 1/0 algorithm
+.SH SYNOPSIS
+.nf
+#include <libsha1.h>
+
+void libsha1_hmac_update(struct libsha1_hmac_state *restrict \fIstate\fP, const void *restrict \fImessage\fP, size_t \fImsglen\fP);
+.fi
+.PP
+Link with
+.IR \-lsha1 .
+.SH DESCRIPTION
+The
+.BR libsha1_hmac_update ()
+function feeds the first
+.I msglen
+.B bits
+(must equivalent to 0 modulus 8) of
+.I message
+into the hashing state of the
+.I state
+parameter.
+.SH RETURN VALUE
+None.
+.SH ERRORS
+None.
+.SH EXAMPLES
+None.
+.SH APPLICATION USAGE
+None.
+.SH RATIONALE
+None.
+.SH FUTURE DIRECTIONS
+None.
+.SH NOTES
+None.
+.SH BUGS
+None.
+.SH SEE ALSO
+.BR libsha1_hmac_digest (3),
+.BR libsha1_hmac_init (3)
diff --git a/libsha1_init.3 b/libsha1_init.3
new file mode 100644
index 0000000..3c38394
--- /dev/null
+++ b/libsha1_init.3
@@ -0,0 +1,62 @@
+.TH LIBSHA1_INIT 3 2019-02-10 libsha1
+.SH NAME
+libsha1_init \- Initialises hashing with a SHA 1/0 algorithm
+.SH SYNOPSIS
+.nf
+#include <libsha1.h>
+
+enum libsha1_algorithm {
+ LIBSHA1_0, /* SHA-0 */
+ LIBSHA1_1 /* SHA-1 */
+};
+
+int libsha1_init(struct libsha1_state *restrict \fIstate\fP, enum libsha1_algorithm \fIalgorithm\fP);
+.fi
+.PP
+Link with
+.IR \-lsha1 .
+.SH DESCRIPTION
+The
+.BR libsha1_init ()
+function stores the selected
+.I algorithm
+in
+.I state
+and initialises
+.I state
+accordingly with the initial hashing state.
+.SH RETURN VALUE
+The
+.BR libsha1_init ()
+function returns 0 upon successful completion,
+otherwise -1 is returned and
+.I errno
+is set appropriately.
+.SH ERRORS
+The
+.BR libsha1_init ()
+function will fail if:
+.TP
+.B EINVAL
+.I algorithm
+is not a valid
+.B enum libsha1_algorithm
+value.
+.SH EXAMPLES
+None.
+.SH APPLICATION USAGE
+None.
+.SH RATIONALE
+None.
+.SH FUTURE DIRECTIONS
+None.
+.SH NOTES
+None.
+.SH BUGS
+None.
+.SH SEE ALSO
+.BR libsha1_digest (3),
+.BR libsha1_marshal (3),
+.BR libsha1_sum_fd (3),
+.BR libsha1_unmarshal (3),
+.BR libsha1_update (3)
diff --git a/libsha1_marshal.3 b/libsha1_marshal.3
new file mode 100644
index 0000000..2b4a643
--- /dev/null
+++ b/libsha1_marshal.3
@@ -0,0 +1,56 @@
+.TH LIBSHA1_MARSHAL 3 2019-02-10 libsha1
+.SH NAME
+libsha1_marshal \- Marshal a SHA 1/0 hashing state
+.SH SYNOPSIS
+.nf
+#include <libsha1.h>
+
+size_t libsha1_marshal(const struct libsha1_state *restrict \fIstate\fP, void *restrict \fIbuf\fP);
+.fi
+.PP
+Link with
+.IR \-lsha1 .
+.SH DESCRIPTION
+The
+.BR libsha1_marshal ()
+function marshal
+.I state
+into the buffer
+.IR buf .
+If the function is called with
+.I NULL
+as
+.IR buf ,
+the required size for
+.I buf
+is returned.
+.PP
+A version number is marshalled into
+.IR buf ,
+this allows new versions of the library to
+unmarshal states marshalled by older versions.
+.SH RETURN VALUE
+The
+.BR libsha1_marshal ()
+function returns the number of marshalled
+bytes (or if
+.I buf
+is
+.IR NULL ,
+the number of bytes that would have been marshalled).
+.SH ERRORS
+None.
+.SH EXAMPLES
+None.
+.SH APPLICATION USAGE
+None.
+.SH RATIONALE
+None.
+.SH FUTURE DIRECTIONS
+None.
+.SH NOTES
+None.
+.SH BUGS
+None.
+.SH SEE ALSO
+.BR libsha1_unmarshal (3)
diff --git a/libsha1_state_output_size.3 b/libsha1_state_output_size.3
new file mode 100644
index 0000000..9058291
--- /dev/null
+++ b/libsha1_state_output_size.3
@@ -0,0 +1,40 @@
+.TH LIBSHA1_STATE_OUTPUT_SIZE 3 2019-02-10 libsha1
+.SH NAME
+libsha1_state_output_size \- Get the size of the output for a SHA 1/0 algorithm
+.SH SYNOPSIS
+.nf
+#include <libsha1.h>
+
+size_t libsha1_state_output_size(const struct libsha1_state *restrict \fIstate\fP);
+.fi
+.PP
+Link with
+.IR \-lsha1 .
+.SH DESCRIPTION
+The
+.BR libsha1_state_output_size ()
+function get the output size for the
+binary output of the hash algorithm
+selected for
+.IR state .
+.SH RETURN VALUE
+The
+.BR libsha1_state_output_size ()
+function returns the output size in bytes,
+a positive number.
+.SH ERRORS
+None.
+.SH EXAMPLES
+None.
+.SH APPLICATION USAGE
+None.
+.SH RATIONALE
+None.
+.SH FUTURE DIRECTIONS
+None.
+.SH NOTES
+None.
+.SH BUGS
+None.
+.SH SEE ALSO
+.BR libsha1_algorithm_output_size (3)
diff --git a/libsha1_sum_fd.3 b/libsha1_sum_fd.3
new file mode 100644
index 0000000..1a14ec3
--- /dev/null
+++ b/libsha1_sum_fd.3
@@ -0,0 +1,73 @@
+.TH LIBSHA1_SUM_FD 3 2019-02-10 libsha1
+.SH NAME
+libsha1_sum_fd \- Hash a file with a SHA 1/0 algorithm
+.SH SYNOPSIS
+.nf
+#include <libsha1.h>
+
+enum libsha1_algorithm {
+ LIBSHA1_0, /* SHA-0 */
+ LIBSHA1_1 /* SHA-1 */
+};
+
+int libsha1_sum_fd(int \fIfd\fP, enum libsha1_algorithm \fIalgorithm\fP, void *restrict \fIhashsum\fP);
+.fi
+.PP
+Link with
+.IR \-lsha1 .
+.SH DESCRIPTION
+The
+.BR libsha1_sum_fd ()
+function hashes the file with the
+file descriptor
+.I fd
+with the selected
+.IR algorithm .
+The resulting hash is stored in binary
+format in
+.IR hashsum .
+The user must make sure that
+.I hashsum
+is sufficiently large, which means at
+least the return value of the
+.BR libsha1_algorithm_output_size (3)
+function.
+.PP
+The
+.BR libsha1_behex_lower (3)
+and
+.BR libsha1_behex_upper (3)
+functions can be used to convert the
+result to hexadecimal format.
+.SH RETURN VALUE
+The
+.BR libsha1_sum_fd ()
+function returns 0 upon successful completion,
+otherwise -1 is returned and
+.I errno
+is set appropriately.
+.SH ERRORS
+The
+.BR libsha1_sum_fd ()
+function may fail for any reason specified for the
+.BR read (3)
+and
+.BR libsha1_init (3)
+functions.
+.SH EXAMPLES
+None.
+.SH APPLICATION USAGE
+None.
+.SH RATIONALE
+None.
+.SH FUTURE DIRECTIONS
+None.
+.SH NOTES
+None.
+.SH BUGS
+None.
+.SH SEE ALSO
+.BR libsha1_algorithm_output_size (3),
+.BR libsha1_behex_lower (3),
+.BR libsha1_behex_upper (3),
+.BR libsha1_init (3)
diff --git a/libsha1_unhex.3 b/libsha1_unhex.3
new file mode 100644
index 0000000..8d92ee2
--- /dev/null
+++ b/libsha1_unhex.3
@@ -0,0 +1,48 @@
+.TH LIBSHA1_UNHEX 3 2019-02-10 libsha1
+.SH NAME
+libsha1_unhex \- Covert hexadecimal to binary
+.SH SYNOPSIS
+.nf
+#include <libsha1.h>
+
+void libsha1_unhex(void *restrict \fIoutput\fP, const char *restrict \fIhashsum\fP);
+.fi
+.PP
+Link with
+.IR \-lsha1 .
+.SH DESCRIPTION
+The
+.BR libsha1_unhex ()
+function converts the content of the string
+.I hashsum
+to binary and stores the result in
+.IR output .
+.PP
+The user must make sure that
+.I hashsum
+is a NUL terminated string with an even number of characters
+and each character is a hexadecimal digit (both lower case
+and upper case, including mixing, is allowed). The output will
+be half the length of this string, and the user must make sure
+that
+.I output
+is at least this size.
+.SH RETURN VALUE
+None.
+.SH ERRORS
+None.
+.SH EXAMPLES
+None.
+.SH APPLICATION USAGE
+None.
+.SH RATIONALE
+None.
+.SH FUTURE DIRECTIONS
+None.
+.SH NOTES
+None.
+.SH BUGS
+None.
+.SH SEE ALSO
+.BR libsha1_behex_lower (3),
+.BR libsha1_behex_upper (3)
diff --git a/libsha1_unmarshal.3 b/libsha1_unmarshal.3
new file mode 100644
index 0000000..4b8ea22
--- /dev/null
+++ b/libsha1_unmarshal.3
@@ -0,0 +1,54 @@
+.TH LIBSHA1_UNMARSHAL 3 2019-02-10 libsha1
+.SH NAME
+libsha1_unmarshal \- Unmarshal a SHA 1/0 hashing state
+.SH SYNOPSIS
+.nf
+#include <libsha1.h>
+
+size_t libsha1_unmarshal(struct libsha1_state *restrict \fIstate\fP, const void *restrict \fIbuf\fP, size_t \fIbufsize\fP);
+.fi
+.PP
+Link with
+.IR \-lsha1 .
+.SH DESCRIPTION
+The
+.BR libsha1_unmarshal ()
+function unmarshal
+.I state
+from the buffer
+.IR buf .
+.I bufsize
+shall be the maximum number of bytes the
+function may read from
+.IR buf .
+.SH RETURN VALUE
+The
+.BR libsha1_unmarshal ()
+function returns the number of unmarshalled
+bytes (this number is always positive) upon
+successful completion, otherwise 0 is returned.
+.SH ERRORS
+The
+.BR libsha1_unmarshal ()
+function will fail if:
+.TP
+.B EINVAL
+.I bufsize
+is too small or the contents of
+.I buf
+is invalid or created with an incompatible
+version of the library.
+.SH EXAMPLES
+None.
+.SH APPLICATION USAGE
+None.
+.SH RATIONALE
+None.
+.SH FUTURE DIRECTIONS
+None.
+.SH NOTES
+None.
+.SH BUGS
+None.
+.SH SEE ALSO
+.BR libsha1_marshal (3)
diff --git a/libsha1_update.3 b/libsha1_update.3
new file mode 100644
index 0000000..febbcb9
--- /dev/null
+++ b/libsha1_update.3
@@ -0,0 +1,43 @@
+.TH LIBSHA1_UPDATE 3 2019-02-10 libsha1
+.SH NAME
+libsha1_update \- Feed data into a SHA 1/0 algorithm
+.SH SYNOPSIS
+.nf
+#include <libsha1.h>
+
+void libsha1_update(struct libsha1_state *restrict \fIstate\fP, const void *restrict \fImessage\fP, size_t \fImsglen\fP);
+.fi
+.PP
+Link with
+.IR \-lsha1 .
+.SH DESCRIPTION
+The
+.BR libsha1_update ()
+function feeds the first
+.I msglen
+.B bits
+(must equivalent to 0 modulus 8) of
+.I message
+into the hashing state of the
+.I state
+parameter.
+.SH RETURN VALUE
+None.
+.SH ERRORS
+None.
+.SH EXAMPLES
+None.
+.SH APPLICATION USAGE
+None.
+.SH RATIONALE
+None.
+.SH FUTURE DIRECTIONS
+None.
+.SH NOTES
+None.
+.SH BUGS
+None.
+.SH SEE ALSO
+.BR libsha1_digest (3),
+.BR libsha1_init (3),
+.BR libsha1_sum_fd (3)
diff --git a/linux.mk b/linux.mk
new file mode 100644
index 0000000..3a44969
--- /dev/null
+++ b/linux.mk
@@ -0,0 +1,5 @@
+LIBEXT = so
+LIBFLAGS = -shared -Wl,-soname,libsha1.$(LIBEXT).$(LIB_MAJOR)
+
+LIBMAJOREXT = $(LIBEXT).$(LIB_MAJOR)
+LIBMINOREXT = $(LIBEXT).$(LIB_MAJOR).$(LIB_MINOR)
diff --git a/macos.mk b/macos.mk
new file mode 100644
index 0000000..47d4b44
--- /dev/null
+++ b/macos.mk
@@ -0,0 +1,5 @@
+LIBEXT = dylib
+LIBFLAGS = -dynamiclib
+
+LIBMAJOREXT = $(LIB_MAJOR).$(LIBEXT)
+LIBMINOREXT = $(LIB_MAJOR).$(LIB_MINOR).$(LIBEXT)
diff --git a/marshal.c b/marshal.c
new file mode 100644
index 0000000..c2d1c11
--- /dev/null
+++ b/marshal.c
@@ -0,0 +1,43 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+/**
+ * Marshal a state into a buffer
+ *
+ * @param state The state to marshal
+ * @param buf Output buffer, `NULL` to only return the required size
+ * @return The number of bytes marshalled to `buf`
+ */
+size_t
+libsha1_marshal(const struct libsha1_state *restrict state, void *restrict buf_)
+{
+ char *restrict buf = buf_;
+ size_t off = 0;
+
+ if (buf)
+ *(int *)buf = 0; /* version */
+ off += sizeof(int);
+ if (buf)
+ *(enum libsha1_algorithm *)&buf[off] = state->algorithm;
+ off += sizeof(enum libsha1_algorithm);
+ if (buf)
+ *(size_t *)&buf[off] = state->message_size;
+ off += sizeof(size_t);
+
+ if (buf)
+ memcpy(&buf[off], state->w, sizeof(state->w));
+ off += sizeof(state->w);
+ if (buf)
+ memcpy(&buf[off], state->h, sizeof(state->h));
+ off += sizeof(state->h);
+
+ if (buf)
+ *(size_t *)&buf[off] = state->chunk_size;
+ off += sizeof(size_t);
+ if (buf)
+ memcpy(&buf[off], state->chunk, (state->message_size / 8) % state->chunk_size);
+ off += (state->message_size / 8) % state->chunk_size;
+
+ return off;
+}
diff --git a/process.c b/process.c
new file mode 100644
index 0000000..ce5a043
--- /dev/null
+++ b/process.c
@@ -0,0 +1,88 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+static inline uint32_t
+rorl(uint32_t n, int k)
+{
+ return (n << k) | (n >> (32 - k));
+}
+
+
+/**
+ * Process a chunk using SHA-1 or SHA-0
+ *
+ * @param state The hashing state
+ * @param chunk The data to process
+ */
+void
+libsha1_process(struct libsha1_state *restrict state, const unsigned char *restrict chunk)
+{
+#define F0(b, c, d) (d ^ (b & (c ^ d)))
+#define F1(b, c, d) (b ^ c ^ d)
+#define F2(b, c, d) ((b & c) | (d & (b | c)))
+#define F3(b, c, d) (b ^ c ^ d)
+#define G0(a, b, c, d, e, i) (e += rorl(a, 5) + F0(b, c, d) + state->w[i] + (uint32_t)0x5A827999UL, b = rorl(b, 30))
+#define G1(a, b, c, d, e, i) (e += rorl(a, 5) + F1(b, c, d) + state->w[i] + (uint32_t)0x6ED9EBA1UL, b = rorl(b, 30))
+#define G2(a, b, c, d, e, i) (e += rorl(a, 5) + F2(b, c, d) + state->w[i] + (uint32_t)0x8F1BBCDCUL, b = rorl(b, 30))
+#define G3(a, b, c, d, e, i) (e += rorl(a, 5) + F3(b, c, d) + state->w[i] + (uint32_t)0xCA62C1D6UL, b = rorl(b, 30))
+
+ uint32_t a, b, c, d, e;
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ state->w[i] = (uint32_t)chunk[4 * i + 0] << 24;
+ state->w[i] |= (uint32_t)chunk[4 * i + 1] << 16;
+ state->w[i] |= (uint32_t)chunk[4 * i + 2] << 8;
+ state->w[i] |= (uint32_t)chunk[4 * i + 3];
+ }
+ for (; i < 80; i++)
+ state->w[i] = rorl(state->w[i - 3] ^ state->w[i - 8] ^ state->w[i - 14] ^ state->w[i - 16], 1);
+ a = state->h[0];
+ b = state->h[1];
+ c = state->h[2];
+ d = state->h[3];
+ e = state->h[4];
+ for (i = 0; i < 20;) {
+ G0(a, b, c, d, e, i++);
+ G0(e, a, b, c, d, i++);
+ G0(d, e, a, b, c, i++);
+ G0(c, d, e, a, b, i++);
+ G0(b, c, d, e, a, i++);
+ }
+ while (i < 40) {
+ G1(a, b, c, d, e, i++);
+ G1(e, a, b, c, d, i++);
+ G1(d, e, a, b, c, i++);
+ G1(c, d, e, a, b, i++);
+ G1(b, c, d, e, a, i++);
+ }
+ while (i < 60) {
+ G2(a, b, c, d, e, i++);
+ G2(e, a, b, c, d, i++);
+ G2(d, e, a, b, c, i++);
+ G2(c, d, e, a, b, i++);
+ G2(b, c, d, e, a, i++);
+ }
+ while (i < 80) {
+ G3(a, b, c, d, e, i++);
+ G3(e, a, b, c, d, i++);
+ G3(d, e, a, b, c, i++);
+ G3(c, d, e, a, b, i++);
+ G3(b, c, d, e, a, i++);
+ }
+ state->h[0] += a;
+ state->h[1] += b;
+ state->h[2] += c;
+ state->h[3] += d;
+ state->h[4] += e;
+
+#undef F0
+#undef F1
+#undef F2
+#undef F3
+#undef G0
+#undef G1
+#undef G2
+#undef G3
+}
diff --git a/state_output_size.c b/state_output_size.c
new file mode 100644
index 0000000..3b3d14d
--- /dev/null
+++ b/state_output_size.c
@@ -0,0 +1,15 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+/**
+ * Get the output size of the algorithm specified for a state
+ *
+ * @param state The state
+ * @return The number of bytes in the output, zero on error
+ */
+size_t
+libsha1_state_output_size(const struct libsha1_state *restrict state)
+{
+ return libsha1_algorithm_output_size(state->algorithm);
+}
diff --git a/sum_fd.c b/sum_fd.c
new file mode 100644
index 0000000..bc2761d
--- /dev/null
+++ b/sum_fd.c
@@ -0,0 +1,45 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+/**
+ * Calculate the checksum for a file,
+ * the content of the file is assumed non-sensitive
+ *
+ * @param fd The file descriptor of the file
+ * @param algorithm The hashing algorithm
+ * @param hashsum Output buffer for the hash
+ * @return Zero on success, -1 on error
+ */
+int
+libsha1_sum_fd(int fd, enum libsha1_algorithm algorithm, void *restrict hashsum)
+{
+ struct libsha1_state state;
+ ssize_t r;
+ struct stat attr;
+ size_t blksize = 4096;
+ char *restrict chunk;
+
+ if (libsha1_init(&state, algorithm) < 0)
+ return -1;
+
+ if (fstat(fd, &attr) == 0 && attr.st_blksize > 0)
+ blksize = (size_t)(attr.st_blksize);
+
+ chunk = alloca(blksize);
+
+ for (;;) {
+ r = read(fd, chunk, blksize);
+ if (r <= 0) {
+ if (!r)
+ break;
+ if (errno == EINTR)
+ continue;
+ return -1;
+ }
+ libsha1_update(&state, chunk, (size_t)r * 8);
+ }
+
+ libsha1_digest(&state, NULL, 0, hashsum);
+ return 0;
+}
diff --git a/test.c b/test.c
new file mode 100644
index 0000000..38b0d85
--- /dev/null
+++ b/test.c
@@ -0,0 +1,309 @@
+/* See LICENSE file for copyright and license details. */
+#include "libsha1.h"
+
+#include <sys/wait.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+
+#define test(EXPR)\
+ do {\
+ if (EXPR)\
+ break;\
+ fprintf(stderr, "Failure at line %i: %s\n", __LINE__, #EXPR);\
+ exit(1);\
+ } while (0)
+
+#define test_str(HAVE, EXPECTED)\
+ do {\
+ if (!strcmp(HAVE, EXPECTED))\
+ break;\
+ fprintf(stderr, "Failure at line %i: expected \"%s\", got \"%s\"\n", __LINE__, EXPECTED, HAVE);\
+ exit(1);\
+ } while (0)
+
+#define test_repeated(CHR, N, ALGO, EXPECTED)\
+ do {\
+ memset(buf, CHR, N);\
+ test(!libsha1_init(&s, ALGO));\
+ libsha1_digest(&s, buf, (N) * 8, buf);\
+ libsha1_behex_lower(str, buf, libsha1_state_output_size(&s));\
+ test_str(str, EXPECTED);\
+ } while (0)
+
+#define test_repeated_huge(CHR, N, ALGO, EXPECTED)\
+ do {\
+ size_t n__ = N;\
+ if (skip_huge)\
+ break;\
+ memset(buf, CHR, sizeof(buf));\
+ test(!libsha1_init(&s, ALGO));\
+ fprintf(stderr, "processing huge message: 0 %%\n");\
+ for (; n__ > sizeof(buf); n__ -= sizeof(buf)) {\
+ libsha1_update(&s, buf, sizeof(buf) * 8);\
+ fprintf(stderr, "\033[A\033[Kprocessing huge message: %zu %%\n", ((N) - n__) * 100 / (N));\
+ }\
+ libsha1_update(&s, buf, n__ * 8);\
+ fprintf(stderr, "\033[A\033[K");\
+ fflush(stderr);\
+ libsha1_digest(&s, NULL, 0, buf);\
+ libsha1_behex_lower(str, buf, libsha1_state_output_size(&s));\
+ test_str(str, EXPECTED);\
+ } while (0)
+
+#define test_custom(S, ALGO, EXPECTED)\
+ do {\
+ test(!libsha1_init(&s, ALGO));\
+ libsha1_digest(&s, S, (sizeof(S) - 1) * 8, buf);\
+ libsha1_behex_lower(str, buf, libsha1_state_output_size(&s));\
+ test_str(str, EXPECTED);\
+ } while (0)
+
+#define test_bits(S, N, ALGO, EXPECTED)\
+ do {\
+ libsha1_unhex(buf, S);\
+ test(!libsha1_init(&s, ALGO));\
+ libsha1_digest(&s, buf, N, buf);\
+ libsha1_behex_lower(str, buf, libsha1_state_output_size(&s));\
+ test_str(str, EXPECTED);\
+ } while (0)
+
+#define test_hmac(ALGO, TEXT, KEY, MAC)\
+ do {\
+ libsha1_unhex(buf, KEY);\
+ test(!libsha1_hmac_init(&hs, ALGO, buf, (sizeof(KEY) - 1) << 2));\
+ libsha1_unhex(buf, TEXT);\
+ libsha1_hmac_digest(&hs, buf, (sizeof(TEXT) - 1) << 2, buf);\
+ libsha1_behex_lower(str, buf, libsha1_hmac_state_output_size(&hs));\
+ test_str(str, MAC);\
+ } while (0)
+
+
+int
+main(int argc, char *argv[])
+{
+ char buf[8096], str[2048];
+ struct libsha1_state s;
+ struct libsha1_hmac_state hs;
+ int skip_huge, fds[2], status;
+ size_t i, j, n, len;
+ ssize_t r;
+ pid_t pid;
+
+ skip_huge = (argc == 2 && !strcmp(argv[1], "skip-huge"));
+
+ libsha1_behex_lower(buf, "", 0);
+ test_str(buf, "");
+
+ libsha1_behex_lower(buf, "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF", 16);
+ test_str(buf, "00112233445566778899aabbccddeeff");
+
+ libsha1_behex_lower(buf, "\x1E\x5A\xC0", 3);
+ test_str(buf, "1e5ac0");
+
+ libsha1_behex_upper(buf, "", 0);
+ test_str(buf, "");
+
+ libsha1_behex_upper(buf, "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF", 16);
+ test_str(buf, "00112233445566778899AABBCCDDEEFF");
+
+ libsha1_behex_upper(buf, "\x1E\x5A\xC0", 3);
+ test_str(buf, "1E5AC0");
+
+ libsha1_unhex(buf, "");
+ test(!memcmp(buf, "", 0));
+
+ libsha1_unhex(buf, "00112233445566778899AABBCCDDEEFF");
+ test(!memcmp(buf, "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF", 16));
+
+ libsha1_unhex(buf, "1E5AC0");
+ test(!memcmp(buf, "\x1E\x5A\xC0", 3));
+
+ libsha1_unhex(buf, "00112233445566778899aabbccddeeff");
+ test(!memcmp(buf, "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF", 16));
+
+ libsha1_unhex(buf, "1e5ac0");
+ test(!memcmp(buf, "\x1E\x5A\xC0", 3));
+
+ libsha1_unhex(buf, "AAbbCcdD");
+ test(!memcmp(buf, "\xAA\xBB\xCC\xDD", 4));
+
+ test(libsha1_algorithm_output_size(LIBSHA1_0) == 20);
+ test(libsha1_algorithm_output_size(LIBSHA1_1) == 20);
+ test(!errno);
+ test(libsha1_algorithm_output_size(~0) == 0); /* should test `errno == EINVAL`, optimising compiler breaks it */
+
+ errno = 0;
+ test(libsha1_init(&s, ~0) == -1 && errno == EINVAL);
+ errno = 0;
+
+#ifdef TODO
+ test(!libsha1_init(&s, LIBSHA1_0));
+ test(libsha1_state_output_size(&s) == 20);
+ libsha1_digest(&s, "", 0, buf);
+ libsha1_behex_lower(str, buf, libsha1_state_output_size(&s));
+ test_str(str, "");
+#endif
+
+ test(!libsha1_init(&s, LIBSHA1_1));
+ test(libsha1_state_output_size(&s) == 20);
+ libsha1_digest(&s, "", 0, buf);
+ libsha1_behex_lower(str, buf, libsha1_state_output_size(&s));
+ test_str(str, "da39a3ee5e6b4b0d3255bfef95601890afd80709");
+
+#ifdef TODO
+ test_repeated(0xFF, 1, LIBSHA1_0, "");
+ test_custom("\xE5\xE0\x99\x24", LIBSHA1_0, "");
+ test_repeated(0x00, 56, LIBSHA1_0, "");
+ test_repeated(0x51, 1000, LIBSHA1_0, "");
+ test_repeated(0x41, 1000, LIBSHA1_0, "");
+ test_repeated(0x99, 1005, LIBSHA1_0, "");
+ test_repeated_huge(0x00, 1000000UL, LIBSHA1_0, "");
+ test_repeated_huge(0x41, 0x20000000UL, LIBSHA1_0, "");
+ test_repeated_huge(0x00, 0x41000000UL, LIBSHA1_0, "");
+ test_repeated_huge(0x84, 0x6000003FUL, LIBSHA1_0, "");
+ test_custom("abc", LIBSHA1_0, "");
+ test_custom("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", LIBSHA1_1,
+ "");
+#endif
+
+ test_repeated(0xFF, 1, LIBSHA1_1, "85e53271e14006f0265921d02d4d736cdc580b0b");
+ test_custom("\xE5\xE0\x99\x24", LIBSHA1_1, "d1dffbc8a175dd8eebe0da87b1792b6dc1018e82");
+ test_repeated(0x00, 56, LIBSHA1_1, "9438e360f578e12c0e0e8ed28e2c125c1cefee16");
+ test_repeated(0x51, 1000, LIBSHA1_1, "49f1cfe3829963158e2b2b2cb5df086cee2e3bb0");
+ test_repeated(0x41, 1000, LIBSHA1_1, "3ae3644d6777a1f56a1defeabc74af9c4b313e49");
+ test_repeated(0x99, 1005, LIBSHA1_1, "18685d56c8bf67c3cee4443e9a78f65c30752f5d");
+ test_repeated_huge(0x00, 1000000UL, LIBSHA1_1, "bef3595266a65a2ff36b700a75e8ed95c68210b6");
+ test_repeated_huge(0x41, 0x20000000UL, LIBSHA1_1, "df3f26fce8fa7bec2c61d0506749a320ac7dc942");
+ test_repeated_huge(0x00, 0x41000000UL, LIBSHA1_1, "320c617b0b6ee1b6f9c3271eae135f40cae22c10");
+ test_repeated_huge(0x84, 0x6000003FUL, LIBSHA1_1, "b20aa99b62e6a480fd93b4d24b2c19ffac649bb8");
+ test_custom("abc", LIBSHA1_1, "a9993e364706816aba3e25717850c26c9cd0d89d");
+ test_custom("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", LIBSHA1_1,
+ "84983e441c3bd26ebaae4aa1f95129e5e54670f1");
+
+ for (i = 0; i < 1000; i++) {
+#ifdef TODO
+ for (j = 0; j < 2; j++) {
+#else
+ for (j = 1; j < 2; j++) {
+#endif
+ memset(buf, 0x41, 1000);
+ test(!libsha1_init(&s, (enum libsha1_algorithm)j));
+ libsha1_update(&s, buf, i * 8);
+ libsha1_digest(&s, buf, (1000 - i) * 8, buf);
+ libsha1_behex_lower(str, buf, libsha1_state_output_size(&s));
+ test_str(str, ((const char *[]){
+ "",
+ "3ae3644d6777a1f56a1defeabc74af9c4b313e49"
+ })[j]);
+
+ memset(buf, 0x41, 1000);
+ test(!libsha1_init(&s, (enum libsha1_algorithm)j));
+ libsha1_update(&s, buf, i * 8);
+ libsha1_update(&s, buf, (1000 - i) * 8);
+ libsha1_digest(&s, NULL, 0, buf);
+ libsha1_behex_lower(str, buf, libsha1_state_output_size(&s));
+ test_str(str, ((const char *[]){
+ "",
+ "3ae3644d6777a1f56a1defeabc74af9c4b313e49"
+ })[j]);
+
+ if (!i)
+ continue;
+
+ memset(buf, 0x41, 1000);
+ test(!libsha1_init(&s, (enum libsha1_algorithm)j));
+ for (n = 0; n + i < 1000; n += i) {
+ libsha1_update(&s, buf, i * 8);
+ test((len = libsha1_marshal(&s, NULL)) && len <= sizeof(str));
+ test(libsha1_marshal(&s, str) == len);
+ memset(&s, 0, sizeof(s));
+ test(libsha1_unmarshal(&s, str, sizeof(str)) == len);
+ }
+ libsha1_digest(&s, buf, (1000 - n) * 8, buf);
+ libsha1_behex_lower(str, buf, libsha1_state_output_size(&s));
+ test_str(str, ((const char *[]){
+ "",
+ "3ae3644d6777a1f56a1defeabc74af9c4b313e49"
+ })[j]);
+ }
+ }
+
+ test(!errno);
+
+ test(!pipe(fds));
+ test((pid = fork()) >= 0);
+ if (!pid) {
+ close(fds[0]);
+ memset(buf, 0x41, 1000);
+ for (n = 1000; n; n -= (size_t)r)
+ test((r = write(fds[1], buf, n < 8 ? n : 8)) > 0);
+ exit(0);
+ }
+ close(fds[1]);
+ test(!libsha1_sum_fd(fds[0], LIBSHA1_1, buf));
+ test(waitpid(pid, &status, 0) == pid);
+ test(!status);
+ close(fds[0]);
+ libsha1_behex_lower(str, buf, libsha1_algorithm_output_size(LIBSHA1_1));
+ test_str(str, "3ae3644d6777a1f56a1defeabc74af9c4b313e49");
+
+ test_bits("00", 1, LIBSHA1_1, "bb6b3e18f0115b57925241676f5b1ae88747b08a");
+ test_bits("01", 2, LIBSHA1_1, "ec6b39952e1a3ec3ab3507185cf756181c84bbe2");
+ test_bits("04", 3, LIBSHA1_1, "a37596ec13a0d2f9e6c0b8b96f9112823aa6d961");
+ test_bits("0d", 4, LIBSHA1_1, "ba582f5967911beb91599684c2eb2baeefb78da7");
+ test_bits("09", 5, LIBSHA1_1, "3320540d1c28b96ddd03eee1b186a8f2ae883fbe");
+ test_bits("08", 6, LIBSHA1_1, "b372bd120957ebc3392cd060e131699d1fee6059");
+ test_bits("22", 7, LIBSHA1_1, "04f31807151181ad0db278a1660526b0aeef64c2");
+
+ test(!libsha1_hmac_init(&hs, LIBSHA1_1, "", 0));
+ test(libsha1_hmac_state_output_size(&hs) == 20);
+ libsha1_hmac_digest(&hs, "", 0, buf);
+ libsha1_behex_lower(str, buf, libsha1_hmac_state_output_size(&hs));
+ test_str(str, "fbdb1d1b18aa6c08324b7d64b71fb76370690e1d");
+
+ test(!libsha1_hmac_init(&hs, LIBSHA1_1, "key", 3 << 3));
+ test(libsha1_hmac_state_output_size(&hs) == 20);
+ libsha1_hmac_digest(&hs, "The quick brown fox jumps over the lazy dog",
+ (sizeof("The quick brown fox jumps over the lazy dog") - 1) << 3, buf);
+ libsha1_behex_lower(str, buf, libsha1_hmac_state_output_size(&hs));
+ test_str(str, "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9");
+
+ n = sizeof("The quick brown fox jumps over the lazy dog") - 1;
+ for (i = 1; i < n; i++) {
+ test(!libsha1_hmac_init(&hs, LIBSHA1_1, "key", 3 << 3));
+ test(libsha1_hmac_state_output_size(&hs) == 20);
+ for (j = 0; j + i < n; j += i) {
+ libsha1_hmac_update(&hs, &"The quick brown fox jumps over the lazy dog"[j], i << 3);
+ test((len = libsha1_hmac_marshal(&hs, NULL)) && len <= sizeof(str));
+ test(libsha1_hmac_marshal(&hs, str) == len);
+ memset(&hs, 0, sizeof(hs));
+ test(libsha1_hmac_unmarshal(&hs, str, sizeof(str)) == len);
+ }
+ libsha1_hmac_digest(&hs, &"The quick brown fox jumps over the lazy dog"[j], (n - j) << 3, buf);
+ libsha1_behex_lower(str, buf, libsha1_hmac_state_output_size(&hs));
+ test_str(str, "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9");
+ }
+
+ test(!errno);
+
+ test_hmac(LIBSHA1_1,
+ "53616d706c65206d65737361676520666f72206b65796c656e3d626c6f636b6c656e",
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+ "5fd596ee78d5553c8ff4e72d266dfd192366da29");
+
+ test_hmac(LIBSHA1_1,
+ "53616d706c65206d65737361676520666f72206b65796c656e3c626c6f636b6c656e",
+ "000102030405060708090a0b0c0d0e0f10111213",
+ "4c99ff0cb1b31bd33f8431dbaf4d17fcd356a807");
+
+ test_hmac(LIBSHA1_1,
+ "53616d706c65206d65737361676520666f72206b65796c656e3d626c6f636b6c656e",
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f60616263",
+ "2d51b2f7750e410584662e38f133435f4c4fd42a");
+
+ return 0;
+}
diff --git a/unhex.c b/unhex.c
new file mode 100644
index 0000000..097b1b8
--- /dev/null
+++ b/unhex.c
@@ -0,0 +1,28 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+/**
+ * Convert a hexadecimal hashsum (both lower case, upper
+ * case and mixed is supported) to binary representation
+ *
+ * @param output Output array, should have an allocation
+ * size of at least `strlen(hashsum) / 2`
+ * @param hashsum The hashsum to convert
+ */
+void
+libsha1_unhex(void *restrict output_, const char *restrict hashsum)
+{
+ unsigned char *restrict output = output_;
+ size_t n = strlen(hashsum) / 2;
+ unsigned char a, b;
+ while (n--) {
+ a = ((const unsigned char *)hashsum)[2 * n + 0];
+ b = ((const unsigned char *)hashsum)[2 * n + 1];
+
+ a = (unsigned char)((a & 15) + (a > '9' ? 9 : 0));
+ b = (unsigned char)((b & 15) + (b > '9' ? 9 : 0));
+
+ output[n] = (unsigned char)((a << 4) | b);
+ }
+}
diff --git a/unmarshal.c b/unmarshal.c
new file mode 100644
index 0000000..e89a8dd
--- /dev/null
+++ b/unmarshal.c
@@ -0,0 +1,59 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+/**
+ * Unmarshal a state from a buffer
+ *
+ * @param state Output parameter for the unmarshalled state
+ * @param buf The buffer from which the state shall be unmarshalled
+ * @param bufsize The maximum number of bytes that can be unmarshalled
+ * @return The number of read bytes, 0 on failure
+ */
+size_t
+libsha1_unmarshal(struct libsha1_state *restrict state, const void *restrict buf_, size_t bufsize)
+{
+ const char *restrict buf = buf_;
+ size_t off = 0;
+
+ if (bufsize < sizeof(int) + sizeof(enum libsha1_algorithm) + sizeof(size_t)) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ if (*(const int *)buf) { /* version */
+ errno = EINVAL;
+ return 0;
+ }
+ off += sizeof(int);
+
+ state->algorithm = *(const enum libsha1_algorithm *)&buf[off];
+ off += sizeof(enum libsha1_algorithm);
+ state->message_size = *(const size_t *)&buf[off];
+ off += sizeof(size_t);
+
+ if (bufsize - off < sizeof(state->w) + sizeof(state->h)) {
+ errno = EINVAL;
+ return 0;
+ }
+ memcpy(state->w, &buf[off], sizeof(state->w));
+ off += sizeof(state->w);
+ memcpy(state->h, &buf[off], sizeof(state->h));
+ off += sizeof(state->h);
+
+ if (bufsize - off < sizeof(size_t)) {
+ errno = EINVAL;
+ return 0;
+ }
+ state->chunk_size = *(const size_t *)&buf[off];
+ off += sizeof(size_t);
+
+ if (bufsize - off < (state->message_size / 8) % state->chunk_size) {
+ errno = EINVAL;
+ return 0;
+ }
+ memcpy(state->chunk, &buf[off], (state->message_size / 8) % state->chunk_size);
+ off += (state->message_size / 8) % state->chunk_size;
+
+ return off;
+}
diff --git a/update.c b/update.c
new file mode 100644
index 0000000..7c9ed9b
--- /dev/null
+++ b/update.c
@@ -0,0 +1,39 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+/**
+ * Absorb more of the message
+ *
+ * @param state The hashing state
+ * @param message The message, in bits, must be equivalent to 0 modulus 8
+ * @param msglen The length of the message
+ */
+void
+libsha1_update(struct libsha1_state *restrict state, const void *restrict message_, size_t msglen)
+{
+ const char *restrict message = message_;
+ size_t n, off;
+
+ off = (state->message_size / 8) % state->chunk_size;
+ state->message_size += msglen;
+ msglen /= 8;
+
+ if (off) {
+ n = msglen < state->chunk_size - off ? msglen : state->chunk_size - off;
+ memcpy(state->chunk + off, message, n);
+ if (off + n == state->chunk_size)
+ libsha1_process(state, state->chunk);
+ message += n;
+ msglen -= n;
+ }
+
+ while (msglen >= state->chunk_size) {
+ libsha1_process(state, (const unsigned char *)message);
+ message += state->chunk_size;
+ msglen -= state->chunk_size;
+ }
+
+ if (msglen)
+ memcpy(state->chunk, message, msglen);
+}