diff options
| author | Mattias Andrée <maandree@kth.se> | 2022-02-13 00:28:37 +0100 | 
|---|---|---|
| committer | Mattias Andrée <maandree@kth.se> | 2022-02-13 00:28:37 +0100 | 
| commit | 2ef79ab9d01090c1b3a2cc445a18d141cbb3c781 (patch) | |
| tree | 1a455cc6d734f4b63adaf7857e4747dd2ed8ffcb | |
| download | libar2simplified-2ef79ab9d01090c1b3a2cc445a18d141cbb3c781.tar.gz libar2simplified-2ef79ab9d01090c1b3a2cc445a18d141cbb3c781.tar.bz2 libar2simplified-2ef79ab9d01090c1b3a2cc445a18d141cbb3c781.tar.xz | |
First commit
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat (limited to '')
| -rw-r--r-- | .gitignore | 15 | ||||
| -rw-r--r-- | LICENSE | 15 | ||||
| -rw-r--r-- | Makefile | 79 | ||||
| -rw-r--r-- | common.h | 33 | ||||
| -rw-r--r-- | config.mk | 8 | ||||
| -rw-r--r-- | libar2simplified.h | 20 | ||||
| -rw-r--r-- | libar2simplified_decode.c | 194 | ||||
| -rw-r--r-- | libar2simplified_encode.c | 59 | ||||
| -rw-r--r-- | libar2simplified_encode_hash.c | 17 | ||||
| -rw-r--r-- | libar2simplified_hash.c | 383 | ||||
| -rw-r--r-- | mk/linux.mk | 4 | ||||
| -rw-r--r-- | mk/macos.mk | 4 | ||||
| -rw-r--r-- | mk/windows.mk | 4 | ||||
| -rw-r--r-- | test.c | 136 | 
14 files changed, 971 insertions, 0 deletions
| 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 @@ -0,0 +1,15 @@ +ISC License + +© 2022 Mattias Andrée <maandree@kth.se> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..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 <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <libar2.h> + + +#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.h> + +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 <sys/random.h> +#endif +#include <time.h> + + +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 <pthread.h> +#include <semaphore.h> + + +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) @@ -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; +} | 
