From 2ef79ab9d01090c1b3a2cc445a18d141cbb3c781 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Sun, 13 Feb 2022 00:28:37 +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 | 15 ++ LICENSE | 15 ++ Makefile | 79 +++++++++ common.h | 33 ++++ config.mk | 8 + libar2simplified.h | 20 +++ libar2simplified_decode.c | 194 +++++++++++++++++++++ libar2simplified_encode.c | 59 +++++++ libar2simplified_encode_hash.c | 17 ++ libar2simplified_hash.c | 383 +++++++++++++++++++++++++++++++++++++++++ mk/linux.mk | 4 + mk/macos.mk | 4 + mk/windows.mk | 4 + test.c | 136 +++++++++++++++ 14 files changed, 971 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 common.h create mode 100644 config.mk create mode 100644 libar2simplified.h create mode 100644 libar2simplified_decode.c create mode 100644 libar2simplified_encode.c create mode 100644 libar2simplified_encode_hash.c create mode 100644 libar2simplified_hash.c create mode 100644 mk/linux.mk create mode 100644 mk/macos.mk create mode 100644 mk/windows.mk create mode 100644 test.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2c489c --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*\#* +*~ +*.o +*.a +*.lo +*.su +*.so +*.so.* +*.dll +*.dylib +*.gch +*.gcov +*.gcno +*.gcda +/test diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d0aa103 --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +ISC License + +© 2022 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..faee51e --- /dev/null +++ b/Makefile @@ -0,0 +1,79 @@ +.POSIX: + +CONFIGFILE = config.mk +include $(CONFIGFILE) + +OS = linux +# Linux: linux +# Mac OS: macos +# Windows: windows +include mk/$(OS).mk + + +LIB_MAJOR = 1 +LIB_MINOR = 0 +LIB_VERSION = $(LIB_MAJOR).$(LIB_MINOR) +LIB_NAME = ar2simplified + + +OBJ =\ + libar2simplified_decode.o\ + libar2simplified_encode.o\ + libar2simplified_encode_hash.o\ + libar2simplified_hash.o + +HDR =\ + libar2simplified.h\ + common.h + +LOBJ = $(OBJ:.o=.lo) + + +all: libar2simplified.a libar2simplified.$(LIBEXT) test +$(OBJ): $(HDR) +$(LOBJ): $(HDR) +test.o: test.c $(HDR) + +.c.o: + $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS) + +.c.lo: + $(CC) -fPIC -c -o $@ $< $(CFLAGS) $(CPPFLAGS) + +test: test.o libar2simplified.a + $(CC) -o $@ test.o libar2simplified.a $(LDFLAGS) + +libar2simplified.a: $(OBJ) + @rm -f -- $@ + $(AR) rc $@ $(OBJ) + +libar2simplified.$(LIBEXT): $(LOBJ) + $(CC) $(LIBFLAGS) -o $@ $(LOBJ) $(LDFLAGS) + +check: test + ./test + +install: libar2simplified.a libar2simplified.$(LIBEXT) + mkdir -p -- "$(DESTDIR)$(PREFIX)/lib" + mkdir -p -- "$(DESTDIR)$(PREFIX)/include" + cp -- libar2simplified.a "$(DESTDIR)$(PREFIX)/lib/" + cp -- libar2simplified.$(LIBEXT) "$(DESTDIR)$(PREFIX)/lib/libar2simplified.$(LIBMINOREXT)" + ln -sf -- libar2simplified.$(LIBMINOREXT) "$(DESTDIR)$(PREFIX)/lib/libar2simplified.$(LIBMAJOREXT)" + ln -sf -- libar2simplified.$(LIBMAJOREXT) "$(DESTDIR)$(PREFIX)/lib/libar2simplified.$(LIBEXT)" + cp -- libar2simplified.h "$(DESTDIR)$(PREFIX)/include/" + +uninstall: + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libar2simplified.a" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libar2simplified.$(LIBMAJOREXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libar2simplified.$(LIBMINOREXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libar2simplified.$(LIBEXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/include/libar2simplified.h" + +clean: + -rm -f -- *.o *.a *.lo *.su *.so *.so.* *.dll *.dylib + -rm -f -- *.gch *.gcov *.gcno *.gcda *.$(LIBEXT) test + +.SUFFIXES: +.SUFFIXES: .lo .o .c + +.PHONY: all check install uninstall clean diff --git a/common.h b/common.h new file mode 100644 index 0000000..cb6a5fe --- /dev/null +++ b/common.h @@ -0,0 +1,33 @@ +/* See LICENSE file for copyright and license details. */ +#include "libar2simplified.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + + +#ifndef ALIGNOF +# ifdef __STDC_VERSION__ +# if __STDC_VERSION__ >= 201112L +# define ALIGNOF(X) _Alignof(X) +# endif +# endif +#endif +#ifndef ALIGNOF +# ifdef __GNUC__ +# define ALIGNOF(X) __alignof__(X) +# else +# define ALIGNOF(X) sizeof(X) +# endif +#endif + + +#ifndef FALLBACK_NPROC +# define FALLBACK_NPROC 4 +#endif diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..eb58289 --- /dev/null +++ b/config.mk @@ -0,0 +1,8 @@ +PREFIX = /usr +MANPREFIX = $(PREFIX)/share/man + +CC = cc + +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE +CFLAGS = -std=c99 -Wall -g -pthread +LDFLAGS = -lar2 -lblake -pthread -lrt diff --git a/libar2simplified.h b/libar2simplified.h new file mode 100644 index 0000000..02a27ec --- /dev/null +++ b/libar2simplified.h @@ -0,0 +1,20 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef LIBAR2SIMPLIFIED_H +#define LIBAR2SIMPLIFIED_H + +#include + +LIBAR2_PUBLIC__ LIBAR2_NONNULL__(1) +char *libar2simplified_encode(const struct libar2_argon2_parameters *params, void *hash); + +LIBAR2_PUBLIC__ LIBAR2_NONNULL__(1, 2) +char *libar2simplified_encode_hash(const struct libar2_argon2_parameters *params, void *hash); + +LIBAR2_PUBLIC__ LIBAR2_NONNULL__(1) +struct libar2_argon2_parameters * +libar2simplified_decode(const char *str, char **tagp, char **endp, int (*random_byte_generator)(char *out, size_t n)); + +LIBAR2_PUBLIC__ LIBAR2_NONNULL__(1, 4) +int libar2simplified_hash(void *hash, void *msg, size_t msglen, struct libar2_argon2_parameters *params); + +#endif diff --git a/libar2simplified_decode.c b/libar2simplified_decode.c new file mode 100644 index 0000000..ab4db5a --- /dev/null +++ b/libar2simplified_decode.c @@ -0,0 +1,194 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifdef __linux__ +#include +#endif +#include + + +static size_t +decode_u32(const char *s, uint_least32_t *outp) +{ + uint_least32_t digit; + size_t i; + + if ((s[0] == '0' && s[1] == '0') || !isdigit(s[0])) { + errno = EINVAL; + return 0; + } + + *outp = 0; + for (i = 0; isdigit(s[i]); i++) { + digit = (uint_least32_t)(s[i] & 15); + if (*outp > ((uint_least32_t)0xFFFFffffUL - digit) / 10) { + errno = ERANGE; + return 0; + } + *outp = *outp * 10 + digit; + } + + return i; +} + + +static int +random_salt(char *out, size_t n, int (*random_byte_generator)(char *out, size_t n)) +{ +#define ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + static int srand_called = 0; + + double x; + size_t i; + int xi; +#ifdef __linux__ + ssize_t r; +#endif + + if (random_byte_generator) { + if (random_byte_generator(out, n)) + return -1; + } else { + i = 0; +#ifdef __linux__ + for(; i < n; i += (size_t)r) { + r = getrandom(&out[i], n - i, GRND_NONBLOCK); + if(r < 0) + break; + } +#endif + if (!srand_called) { + srand((unsigned int)time(NULL)); + srand_called = 1; + } + for(; i < n; i++) { + xi = rand(); + x = (double)xi; + x /= (double)RAND_MAX; + x *= 63; + out[i] = (char)x; + } + } + + for (i = 0; i < n; i++) + out[i] = ALPHABET[out[i] % 64]; + return 0; +} + + +static void * +allocate(size_t num, size_t size, size_t alignment, struct libar2_context *ctx) +{ + (void) ctx; + (void) alignment; + return malloc(num * size); +} + + +static void +deallocate(void *ptr, struct libar2_context *ctx) +{ + (void) ctx; + free(ptr); +} + + +struct libar2_argon2_parameters * +libar2simplified_decode(const char *str, char **tagp, char **endp, int (*random_byte_generator)(char *out, size_t n)) +{ + struct libar2_argon2_parameters params, *ret; + const char *p = str; + const char *end; + char *str_free = NULL; + char *buf = NULL; + size_t n, saltsize, offset; + uint_least32_t saltlen, hashlen; + struct libar2_context ctx; + + if (*p != '$') + goto einval; + p = strchr(&p[1], '$'); + if (!p) + goto einval; + if (p[1] == 'v' && p[2] == '=') { + p = strchr(&p[1], '$'); + if (!p) + goto einval; + } + p = strchr(&p[1], '$'); + if (!p) + goto einval; + p++; + end = strchr(p, '$'); + if (!end) + goto einval; + + if (*p == '*') { + n = decode_u32(&p[1], &saltlen); + if (!n++) + goto fail; + if (&p[n] != end) + goto einval; + params.saltlen = (size_t)saltlen; + saltsize = libar2_encode_base64(NULL, NULL, saltlen) - 1; + offset = (size_t)(p - str); + str_free = malloc(offset + saltsize + strlen(&p[n]) + 1); + if (!str_free) + goto enomem; + memcpy(str_free, str, offset); + if (random_salt(&str_free[offset], saltsize, random_byte_generator)) + goto fail; + offset += saltsize; + stpcpy(&str_free[offset], &p[n]); + str = str_free; + } + end++; + + ctx.allocate = allocate; + ctx.deallocate = deallocate; + + if (!libar2_decode_params(str, ¶ms, &buf, &ctx)) + goto fail; + + if (*end == '*') { + n = decode_u32(&end[1], &hashlen); + if (!n++) + goto fail; + end = &end[n]; + params.hashlen = (size_t)hashlen; + if (tagp) + *tagp = NULL; + } else { + if (tagp) + *tagp = *(void **)(void *)&end; + end = &end[libar2_encode_base64(NULL, NULL, params.hashlen) - 1]; + } + + ret = malloc(sizeof(params) + params.saltlen); + if (!ret) + goto enomem; + memcpy(ret, ¶ms, sizeof(params)); + if (buf) { + memcpy(&((char *)ret)[sizeof(params)], buf, params.saltlen); + ret->salt = &((unsigned char *)ret)[sizeof(params)]; + deallocate(buf, &ctx); + } + + if (endp) + *endp = *(void **)(void *)&end; + + free(str_free); + return ret; + +einval: + errno = EINVAL; + return NULL; + +fail: + free(str_free); + return NULL; + +enomem: + free(str_free); + errno = ENOMEM; + return NULL; +} diff --git a/libar2simplified_encode.c b/libar2simplified_encode.c new file mode 100644 index 0000000..054e574 --- /dev/null +++ b/libar2simplified_encode.c @@ -0,0 +1,59 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +static size_t +encode_params(char *buf, size_t bufsize, const struct libar2_argon2_parameters *params) +{ + if (params->salt) { + return libar2_encode_params(buf, params); + } + + return 1 + (size_t)snprintf(buf, bufsize, "$%s$v=%i$m=%lu,t=%lu,p=%lu$*%zu$", + libar2_type_to_string(params->type, LIBAR2_LOWER_CASE), + (int)params->version, + (unsigned long int)params->m_cost, + (unsigned long int)params->t_cost, + (unsigned long int)params->lanes, + params->saltlen); +} + + +char * +libar2simplified_encode(const struct libar2_argon2_parameters *params_, void *hash) +{ + struct libar2_argon2_parameters params = *params_; + size_t size, off; + char *ret; + + if (libar2_validate_params(¶ms, NULL) != LIBAR2_OK) { + errno = EINVAL; + return NULL; + } + + size = encode_params(NULL, 0, ¶ms); + if (hash) + size += libar2_encode_base64(NULL, NULL, params.hashlen) - 1; + else + size += (size_t)snprintf(NULL, 0, "*%zu", params.hashlen); + + ret = malloc(size); + if (!ret) { + errno = ENOMEM; + return NULL; + } + + off = encode_params(ret, size, ¶ms) - 1; + if (off > size - 1) + abort(); + + if (hash) + off += libar2_encode_base64(&ret[off], hash, params.hashlen) - 1; + else + off += (size_t)sprintf(&ret[off], "*%zu", params.hashlen); + + if (off > size - 1) + abort(); + + return ret; +} diff --git a/libar2simplified_encode_hash.c b/libar2simplified_encode_hash.c new file mode 100644 index 0000000..da80ff6 --- /dev/null +++ b/libar2simplified_encode_hash.c @@ -0,0 +1,17 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +char * +libar2simplified_encode_hash(const struct libar2_argon2_parameters *params, void *hash) +{ + size_t size = libar2_encode_base64(NULL, hash, params->hashlen); + char *ret = malloc(size); + if (!ret) { + errno = ENOMEM; + return NULL; + } + if (libar2_encode_base64(ret, hash, params->hashlen) != size) + abort(); + return ret; +} diff --git a/libar2simplified_hash.c b/libar2simplified_hash.c new file mode 100644 index 0000000..bdc5dd2 --- /dev/null +++ b/libar2simplified_hash.c @@ -0,0 +1,383 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#include +#include + + +struct thread_data { + pthread_t thread; + pthread_mutex_t mutex; + sem_t semaphore; + pthread_mutex_t *master_mutex; + sem_t *master_semaphore; + int *master_needs_a_thread; + int error; + void (*function)(void *data); + void *function_input; +}; + +struct user_data { + struct thread_data *threads; + size_t nthreads; + int need_a_thread; + pthread_mutex_t master_mutex; + sem_t master_semaphore; +}; + + +static void * +alignedalloc(size_t num, size_t size, size_t alignment, size_t extra) +{ + void *ptr; + int err; + if (num > (SIZE_MAX - extra) / size) { + errno = ENOMEM; + return NULL; + } + if (alignment < sizeof(void *)) + alignment = sizeof(void *); + err = posix_memalign(&ptr, alignment, num * size + extra); + if (err) { + errno = err; + return NULL; + } else { + return ptr; + } +} + + +static void * +allocate(size_t num, size_t size, size_t alignment, struct libar2_context *ctx) +{ + size_t pad = (alignment - ((2 * sizeof(size_t)) & (alignment - 1))) & (alignment - 1); + char *ptr = alignedalloc(num, size, alignment, pad + 2 * sizeof(size_t)); + if (ptr) { + ptr = &ptr[pad]; + *(size_t *)ptr = pad; + ptr = &ptr[sizeof(size_t)]; + *(size_t *)ptr = num * size; + ptr = &ptr[sizeof(size_t)]; + } + (void) ctx; + return ptr; +} + + +static void +deallocate(void *ptr, struct libar2_context *ctx) +{ + char *p = ptr; + p -= sizeof(size_t); + libar2_erase(ptr, *(size_t *)p); + p -= sizeof(size_t); + p -= *(size_t *)p; + free(p); + (void) ctx; +} + + +static void * +thread_loop(void *data_) +{ + struct thread_data *data = data_; + int err; + void (*function)(void *data); + void *function_input; + + for (;;) { + if (sem_wait(&data->semaphore)) { + data->error = errno; + return NULL; + } + + err = pthread_mutex_lock(&data->mutex); + if (err) { + data->error = err; + return NULL; + } + function_input = data->function_input; + function = data->function; + pthread_mutex_unlock(&data->mutex); + + if (function) { + function(function_input); + + err = pthread_mutex_lock(data->master_mutex); + if (err) { + data->error = err; + return NULL; + } + + err = pthread_mutex_lock(&data->mutex); + if (err) { + pthread_mutex_unlock(data->master_mutex); + data->error = err; + return NULL; + } + data->function = NULL; + data->function_input = NULL; + pthread_mutex_unlock(&data->mutex); + if (*data->master_needs_a_thread) { + *data->master_needs_a_thread = 0; + if (sem_post(data->master_semaphore)) { + err = errno; + pthread_mutex_unlock(data->master_mutex); + data->error = err; + return NULL; + } + } + pthread_mutex_unlock(data->master_mutex); + } + } +} + + +static int +run_thread(size_t index, void (*function)(void *arg), void *arg, struct libar2_context *ctx) +{ + struct user_data *data = ctx->user_data; + int err; + err = pthread_mutex_lock(&data->threads[index].mutex); + if (err) { + errno = err; + return -1; + } + if (data->threads[index].error) { + err = data->threads[index].error; + pthread_mutex_unlock(&data->threads[index].mutex); + errno = err; + return -1; + } + data->threads[index].function_input = arg; + data->threads[index].function = function; + if (sem_post(&data->threads[index].semaphore)) { + return -1; + } + pthread_mutex_unlock(&data->threads[index].mutex); + return 0; +} + + +static int +destroy_thread_pool(struct libar2_context *ctx) +{ + struct user_data *data = ctx->user_data; + size_t i; + int ret = 0, err; + for (i = data->nthreads; i--;) + if (run_thread(i, pthread_exit, NULL, ctx)) + return -1; + for (i = data->nthreads; i--;) { + pthread_join(data->threads[i].thread, NULL); + err = pthread_mutex_lock(&data->threads[i].mutex); + if (err) + ret = err; + sem_destroy(&data->threads[i].semaphore); + if (data->threads[i].error) + ret = data->threads[i].error; + pthread_mutex_unlock(&data->threads[i].mutex); + pthread_mutex_destroy(&data->threads[i].mutex); + } + free(data->threads); + sem_destroy(&data->master_semaphore); + pthread_mutex_destroy(&data->master_mutex); + return ret; +} + + +static int +init_thread_pool(size_t desired, size_t *createdp, struct libar2_context *ctx) +{ + struct user_data *data = ctx->user_data; + int err; + size_t i; + long int nproc, nproc_limit; +#ifdef __linux__ + char path[sizeof("/sys/devices/system/cpu/cpu") + 3 * sizeof(nproc)]; +#endif + +#ifdef TODO + if (desired < 2) { + *createdp = 0; + return 0; + } +#endif + + nproc = sysconf(_SC_NPROCESSORS_ONLN); +#ifdef __linux__ + if (nproc < 1) { + nproc_limit = desired > LONG_MAX ? LONG_MAX : (long int)desired; + for (nproc = 0; nproc < nproc_limit; nproc++) { + sprintf(path, "%s%li", "/sys/devices/system/cpu/cpu", nproc); + if (access(path, F_OK)) + break; + } + } +#endif + if (nproc < 1) + nproc = FALLBACK_NPROC; + + if (nproc == 1) { + *createdp = 0; + return 0; + } + + data->nthreads = (size_t)nproc < desired ? (size_t)nproc : desired; + *createdp = data->nthreads; + + data->threads = alignedalloc(data->nthreads, sizeof(*data->threads), ALIGNOF(struct thread_data), 0); + if (!data->threads) + return -1; + + err = pthread_mutex_init(&data->master_mutex, NULL); + if (err) { + free(data->threads); + return -1; + } + err = sem_init(&data->master_semaphore, 0, 0); + if (err) { + pthread_mutex_destroy(&data->master_mutex); + free(data->threads); + return -1; + } + data->need_a_thread = 0; + + for (i = 0; i < data->nthreads; i++) { + memset(&data->threads[i], 0, sizeof(data->threads[i])); + data->threads[i].master_mutex = &data->master_mutex; + data->threads[i].master_semaphore = &data->master_semaphore; + data->threads[i].master_needs_a_thread = &data->need_a_thread; + err = pthread_mutex_init(&data->threads[i].mutex, NULL); + if (err) + goto fail_post_mutex; + if (sem_init(&data->threads[i].semaphore, 0, 0)) { + err = errno; + goto fail_post_cond; + } + err = pthread_create(&data->threads[i].thread, NULL, thread_loop, &data->threads[i]); + if (err) { + sem_destroy(&data->threads[i].semaphore); + fail_post_cond: + pthread_mutex_destroy(&data->threads[i].mutex); + fail_post_mutex: + data->nthreads = i; + destroy_thread_pool(ctx); + errno = err; + return -1; + } + } + + return 0; +} + + +static int +set_need_a_thread(struct user_data *data, int need) +{ + int err; + err = pthread_mutex_lock(&data->master_mutex); + if (err) { + errno = err; + return -1; + } + data->need_a_thread = need; + pthread_mutex_unlock(&data->master_mutex); + return 0; +} + + +static int +await_some_thread(struct user_data *data) +{ + int err, need_a_thread; + err = pthread_mutex_lock(&data->master_mutex); + if (err) { + errno = err; + return -1; + } + need_a_thread = data->need_a_thread; + pthread_mutex_unlock(&data->master_mutex); + if (need_a_thread) { + if (sem_wait(&data->master_semaphore)) { + err = errno; + pthread_mutex_unlock(&data->master_mutex); + errno = err; + return -1; + } + } + return 0; +} + + +static size_t +await_threads(size_t *indices, size_t n, size_t require, struct libar2_context *ctx) +{ + struct user_data *data = ctx->user_data; + size_t i, ret = 0, first = 0; + int err; + for (;;) { + if (set_need_a_thread(data, 1)) + return 0; + for (i = first; i < data->nthreads; i++) { + err = pthread_mutex_lock(&data->threads[i].mutex); + if (err) { + errno = err; + return 0; + } + if (!data->threads[i].function) { + if (ret++ < n) + indices[ret - 1] = i; + first += (i == first); + } + if (data->threads[i].error) { + errno = data->threads[i].error; + return 0; + } + pthread_mutex_unlock(&data->threads[i].mutex); + } + if (ret >= require) { + if (set_need_a_thread(data, 0)) + return 0; + return ret; + } + if (await_some_thread(data)) + return 0; + } +} + + +static size_t +get_ready_threads(size_t *indices, size_t n, struct libar2_context *ctx) +{ + return await_threads(indices, n, 1, ctx); +} + + +static int +join_thread_pool(struct libar2_context *ctx) +{ + struct user_data *data = ctx->user_data; + return await_threads(NULL, 0, data->nthreads, ctx) ? 0 : -1; +} + + +int +libar2simplified_hash(void *hash, void *msg, size_t msglen, struct libar2_argon2_parameters *params) +{ + struct user_data ctx_data; + struct libar2_context ctx; + + memset(&ctx, 0, sizeof(ctx)); + ctx.user_data = &ctx_data; + ctx.autoerase_message = 1; + ctx.autoerase_salt = 1; + ctx.allocate = allocate; + ctx.deallocate = deallocate; + ctx.init_thread_pool = init_thread_pool; + ctx.get_ready_threads = get_ready_threads; + ctx.run_thread = run_thread; + ctx.join_thread_pool = join_thread_pool; + ctx.destroy_thread_pool = destroy_thread_pool; + + return libar2_hash(hash, msg, msglen, params, &ctx); +} diff --git a/mk/linux.mk b/mk/linux.mk new file mode 100644 index 0000000..d016d31 --- /dev/null +++ b/mk/linux.mk @@ -0,0 +1,4 @@ +LIBEXT = so +LIBFLAGS = -shared -Wl,-soname,lib$(LIB_NAME).$(LIBEXT).$(LIB_MAJOR) +LIBMAJOREXT = $(LIBEXT).$(LIB_MAJOR) +LIBMINOREXT = $(LIBEXT).$(LIB_VERSION) diff --git a/mk/macos.mk b/mk/macos.mk new file mode 100644 index 0000000..bd92de6 --- /dev/null +++ b/mk/macos.mk @@ -0,0 +1,4 @@ +LIBEXT = dylib +LIBFLAGS = -dynamiclib +LIBMAJOREXT = $(LIB_MAJOR).$(LIBEXT) +LIBMINOREXT = $(LIB_VERSION).$(LIBEXT) diff --git a/mk/windows.mk b/mk/windows.mk new file mode 100644 index 0000000..e9602e1 --- /dev/null +++ b/mk/windows.mk @@ -0,0 +1,4 @@ +LIBEXT = dll +LIBFLAGS = -mdll +LIBMAJOREXT = $(LIB_MAJOR).$(LIBEXT) +LIBMINOREXT = $(LIB_VERSION).$(LIBEXT) diff --git a/test.c b/test.c new file mode 100644 index 0000000..6c4bf6f --- /dev/null +++ b/test.c @@ -0,0 +1,136 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +#define MEM(S) S, sizeof(S) - 1 + + +#define assert(TRUTH) assert_(TRUTH, #TRUTH, __LINE__) +#define assert_streq(RESULT, EXPECT) assert_streq_(RESULT, EXPECT, #RESULT, __LINE__) +#define assert_zueq(RESULT, EXPECT) assert_zueq_(RESULT, EXPECT, #RESULT, __LINE__) + +static int from_lineno = 0; + + +static int +nulstrcmp(const char *a, const char *b) +{ + return !a ? -!!b : !b ? +1 : strcmp(a, b); +} + + +static void +assert_(int truth, const char *truthstr, int lineno) +{ + if (!truth) { + if (from_lineno) + fprintf(stderr, "Assertion at line %i, from line %i failed: %s\n", lineno, from_lineno, truthstr); + else + fprintf(stderr, "Assertion at line %i failed: %s\n", lineno, truthstr); + fprintf(stderr, "\terrno: %i (%s)\n", errno, strerror(errno)); + exit(1); + } +} + + +static void +assert_streq_(const char *result, const char *expect, const char *code, int lineno) +{ + if (nulstrcmp(result, expect)) { + if (from_lineno) + fprintf(stderr, "Assertion at line %i, form line %i failed:\n", lineno, from_lineno); + else + fprintf(stderr, "Assertion at line %i failed:\n", lineno); + fprintf(stderr, "\tcode: %s\n", code); + fprintf(stderr, "\tresult: %s\n", result); + fprintf(stderr, "\texpected: %s\n", expect); + fprintf(stderr, "\terrno: %i (%s)\n", errno, strerror(errno)); + exit(1); + } +} + + +static void +assert_zueq_(size_t result, size_t expect, const char *code, int lineno) +{ + if (result != expect) { + if (from_lineno) + fprintf(stderr, "Assertion at line %i, form line %i failed:\n", lineno, from_lineno); + else + fprintf(stderr, "Assertion at line %i failed:\n", lineno); + fprintf(stderr, "\tcode: %s\n", code); + fprintf(stderr, "\tresult: %zu\n", result); + fprintf(stderr, "\texpected: %zu\n", expect); + fprintf(stderr, "\terrno: %i (%s)\n", errno, strerror(errno)); + exit(1); + } +} + + +static void +check_hash(const char *pwd_, size_t pwdlen, const char *hash, int lineno) +{ + struct libar2_argon2_parameters *params; + char *output[512], pwd[512], *tag_expect, *tag_got, *paramstr; + size_t taglen; + + from_lineno = lineno; + errno = 0; + + strcpy(pwd, pwd_); + + assert(!!(params = libar2simplified_decode(hash, &tag_expect, NULL, NULL))); + assert_zueq(libar2_decode_base64(tag_expect, output, &taglen), strlen(tag_expect)); + assert_zueq(taglen, params->hashlen); + assert(!!(paramstr = libar2simplified_encode(params, output))); + assert_streq(paramstr, hash); + free(paramstr); + + assert(!libar2simplified_hash(output, pwd, pwdlen, params)); + tag_got = libar2simplified_encode_hash(params, output); + free(params); + assert_streq(tag_got, tag_expect); + free(tag_got); + + from_lineno = 0; +} + + +int +main(void) +{ +#define CHECK(PWD, HASH)\ + check_hash(MEM(PWD), HASH, __LINE__) + + CHECK("\x00", "$argon2d$v=16$m=8,t=1,p=1$ICAgICAgICA$Eyx1BxGazSuPQoy7osaQuo20Dw9VI97dYUOgcC3cMgw"); + CHECK("test", "$argon2i$v=19$m=4096,t=3,p=1$fn5/f35+f38$9tqKA4WMEsSAOEUwatjxvJLSqL1j0GQkgbsfnpresDw"); + CHECK("\x00", "$argon2id$v=16$m=8,t=1,p=1$ICAgICAgICA$fXq1aUbp9yhbn+EQc4AzUUE6AKnHAkvzIXsN6J4ukvE"); + CHECK("", "$argon2d$v=16$m=8,t=1,p=1$ICAgICAgICA$X54KZYxUSfMUihzebb70sKbheabHilo8gsUldrVU4IU"); + CHECK("", "$argon2d$v=16$m=8,t=1,p=1$ICAgICAgICA$NjODMrWrS7zeivNNpHsuxD9c6uDmUQ6YqPRhb8H5DSNw9n683FUCJZ3tyxgfJpYYANI+01WT/S5zp1UVs+qNRwnkdEyLKZMg+DIOXVc9z1po9ZlZG8+Gp4g5brqfza3lvkR9vw"); + CHECK("", "$argon2ds$v=16$m=8,t=1,p=1$ICAgICAgICA$zgdykk9ZjN5VyrW0LxGw8LmrJ1Z6fqSC+3jPQtn4n0s"); + + CHECK("password", "$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"); + CHECK("password", "$argon2i$m=256,t=2,p=1$c29tZXNhbHQ$/U3YPXYsSb3q9XxHvc0MLxur+GP960kN9j7emXX8zwY"); + CHECK("password", "$argon2i$m=65536,t=1,p=1$c29tZXNhbHQ$gWMFUrjzsfSM2xmSxMZ4ZD1JCytetP9sSzQ4tWIXJLI"); + CHECK("password", "$argon2i$m=65536,t=4,p=1$c29tZXNhbHQ$8hLwFhXm6110c03D70Ct4tUdBSRo2MaUQKOh8sHChHs"); + CHECK("differentpassword", "$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ$6ckCB0tnVFMaOgvlGeW69ASzDOabPwGsO/ISKZYBCaM"); + CHECK("password", "$argon2i$m=65536,t=2,p=1$ZGlmZnNhbHQ$eaEDuQ/orvhXDLMfyLIiWXeJFvgza3vaw4kladTxxJc"); + + CHECK("password", "$argon2i$v=16$m=256,t=2,p=1$c29tZXNhbHQ$/U3YPXYsSb3q9XxHvc0MLxur+GP960kN9j7emXX8zwY"); + + CHECK("password", "$argon2i$v=19$m=256,t=2,p=1$c29tZXNhbHQ$iekCn0Y3spW+sCcFanM2xBT63UP2sghkUoHLIUpWRS8"); + CHECK("password", "$argon2i$v=19$m=65536,t=1,p=1$c29tZXNhbHQ$0WgHXE2YXhPr6uVgz4uUw7XYoWxRkWtvSsLaOsEbvs8"); + CHECK("differentpassword", "$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$FK6NoBr+qHAMI1jc73xTWNkCEoK9iGY6RWL1n7dNIu4"); + + CHECK("password", "$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"); + CHECK("password", "$argon2id$v=19$m=65536,t=1,p=1$c29tZXNhbHQ$9qWtwbpyPd3vm1rB1GThgPzZ3/ydHL92zKL+15XZypg"); + CHECK("password", "$argon2id$v=19$m=65536,t=4,p=1$c29tZXNhbHQ$kCXUjmjvc5XMqQedpMTsOv+zyJEf5PhtGiUghW9jFyw"); + CHECK("differentpassword", "$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$C4TWUs9rDEvq7w3+J4umqA32aWKB1+DSiRuBfYxFj94"); + + CHECK("password", "$argon2i$m=256,t=2,p=2$c29tZXNhbHQ$tsEVYKap1h6scGt5ovl9aLRGOqOth+AMB+KwHpDFZPs"); + CHECK("password", "$argon2i$v=16$m=256,t=2,p=2$c29tZXNhbHQ$tsEVYKap1h6scGt5ovl9aLRGOqOth+AMB+KwHpDFZPs"); + CHECK("password", "$argon2i$v=19$m=256,t=2,p=2$c29tZXNhbHQ$T/XOJ2mh1/TIpJHfCdQan76Q5esCFVoT5MAeIM1Oq2E"); + CHECK("password", "$argon2id$v=19$m=256,t=2,p=2$c29tZXNhbHQ$bQk8UB/VmZZF4Oo79iDXuL5/0ttZwg2f/5U52iv1cDc"); + + return 0; +} -- cgit v1.2.3-70-g09d2