From ed0296b9055713df0d910e4e7528ffe6fc539514 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Sun, 10 Feb 2019 20:21:19 +0100 Subject: First commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- .gitignore | 12 ++ LICENSE | 15 ++ Makefile | 117 ++++++++++++++ README | 12 ++ algorithm_output_size.c | 21 +++ behex_lower.c | 21 +++ behex_upper.c | 21 +++ common.h | 21 +++ config.mk | 6 + digest.c | 61 ++++++++ hmac_digest.c | 33 ++++ hmac_init.c | 53 +++++++ hmac_marshal.c | 41 +++++ hmac_state_output_size.c | 15 ++ hmac_unmarshal.c | 54 +++++++ hmac_update.c | 23 +++ init.c | 46 ++++++ libsha1.h | 328 +++++++++++++++++++++++++++++++++++++++ libsha1.h.0 | 134 ++++++++++++++++ libsha1_algorithm_output_size.3 | 53 +++++++ libsha1_behex_lower.3 | 49 ++++++ libsha1_behex_upper.3 | 49 ++++++ libsha1_digest.3 | 70 +++++++++ libsha1_hmac_digest.3 | 70 +++++++++ libsha1_hmac_init.3 | 66 ++++++++ libsha1_hmac_marshal.3 | 56 +++++++ libsha1_hmac_state_output_size.3 | 40 +++++ libsha1_hmac_unmarshal.3 | 54 +++++++ libsha1_hmac_update.3 | 42 +++++ libsha1_init.3 | 62 ++++++++ libsha1_marshal.3 | 56 +++++++ libsha1_state_output_size.3 | 40 +++++ libsha1_sum_fd.3 | 73 +++++++++ libsha1_unhex.3 | 48 ++++++ libsha1_unmarshal.3 | 54 +++++++ libsha1_update.3 | 43 +++++ linux.mk | 5 + macos.mk | 5 + marshal.c | 43 +++++ process.c | 88 +++++++++++ state_output_size.c | 15 ++ sum_fd.c | 45 ++++++ test.c | 309 ++++++++++++++++++++++++++++++++++++ unhex.c | 28 ++++ unmarshal.c | 59 +++++++ update.c | 39 +++++ 46 files changed, 2595 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README create mode 100644 algorithm_output_size.c create mode 100644 behex_lower.c create mode 100644 behex_upper.c create mode 100644 common.h create mode 100644 config.mk create mode 100644 digest.c create mode 100644 hmac_digest.c create mode 100644 hmac_init.c create mode 100644 hmac_marshal.c create mode 100644 hmac_state_output_size.c create mode 100644 hmac_unmarshal.c create mode 100644 hmac_update.c create mode 100644 init.c create mode 100644 libsha1.h create mode 100644 libsha1.h.0 create mode 100644 libsha1_algorithm_output_size.3 create mode 100644 libsha1_behex_lower.3 create mode 100644 libsha1_behex_upper.3 create mode 100644 libsha1_digest.3 create mode 100644 libsha1_hmac_digest.3 create mode 100644 libsha1_hmac_init.3 create mode 100644 libsha1_hmac_marshal.3 create mode 100644 libsha1_hmac_state_output_size.3 create mode 100644 libsha1_hmac_unmarshal.3 create mode 100644 libsha1_hmac_update.3 create mode 100644 libsha1_init.3 create mode 100644 libsha1_marshal.3 create mode 100644 libsha1_state_output_size.3 create mode 100644 libsha1_sum_fd.3 create mode 100644 libsha1_unhex.3 create mode 100644 libsha1_unmarshal.3 create mode 100644 libsha1_update.3 create mode 100644 linux.mk create mode 100644 macos.mk create mode 100644 marshal.c create mode 100644 process.c create mode 100644 state_output_size.c create mode 100644 sum_fd.c create mode 100644 test.c create mode 100644 unhex.c create mode 100644 unmarshal.c create mode 100644 update.c 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 + +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 +#include +#include +#include +#include +#include + + +/** + * 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 +#include + + +/** + * 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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 +#include +#include +#include +#include +#include + + +#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); +} -- cgit v1.2.3-70-g09d2