diff options
Diffstat (limited to '')
| -rw-r--r-- | .gitignore | 15 | ||||
| -rw-r--r-- | LICENSE | 15 | ||||
| -rw-r--r-- | Makefile | 88 | ||||
| -rw-r--r-- | common.h | 64 | ||||
| -rw-r--r-- | config.mk | 8 | ||||
| -rw-r--r-- | libar2.h | 618 | ||||
| -rw-r--r-- | libar2_decode_base64.c | 68 | ||||
| -rw-r--r-- | libar2_decode_params.c | 147 | ||||
| -rw-r--r-- | libar2_earse.c | 38 | ||||
| -rw-r--r-- | libar2_encode_base64.c | 42 | ||||
| -rw-r--r-- | libar2_encode_params.c | 31 | ||||
| -rw-r--r-- | libar2_hash.c | 612 | ||||
| -rw-r--r-- | libar2_latest_argon2_version.c | 5 | ||||
| -rw-r--r-- | libar2_string_to_type.c | 30 | ||||
| -rw-r--r-- | libar2_string_to_version.c | 20 | ||||
| -rw-r--r-- | libar2_type_to_string.c | 19 | ||||
| -rw-r--r-- | libar2_validate_params.c | 20 | ||||
| -rw-r--r-- | libar2_version_to_string.c | 18 | ||||
| -rw-r--r-- | libar2_version_to_string_proper.c | 18 | ||||
| -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 | 736 | 
23 files changed, 2624 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..ea08bac --- /dev/null +++ b/Makefile @@ -0,0 +1,88 @@ +.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 = ar2 + + +OBJ =\ +	libar2_decode_base64.o\ +	libar2_decode_params.o\ +	libar2_encode_base64.o\ +	libar2_encode_params.o\ +	libar2_earse.o\ +	libar2_hash.o\ +	libar2_latest_argon2_version.o\ +	libar2_string_to_type.o\ +	libar2_string_to_version.o\ +	libar2_type_to_string.o\ +	libar2_validate_params.o\ +	libar2_version_to_string.o\ +	libar2_version_to_string_proper.o + +HDR =\ +	libar2.h\ +	common.h + +LOBJ = $(OBJ:.o=.lo) + + +all: libar2.a libar2.$(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 libar2.a +	$(CC) -o $@ test.o libar2.a $(LDFLAGS) + +libar2.a: $(OBJ) +	@rm -f -- $@ +	$(AR) rc $@ $(OBJ) + +libar2.$(LIBEXT): $(LOBJ) +	$(CC) $(LIBFLAGS) -o $@ $(LOBJ) $(LDFLAGS) + +check: test +	./test + +install: libar2.a libar2.$(LIBEXT) +	mkdir -p -- "$(DESTDIR)$(PREFIX)/lib" +	mkdir -p -- "$(DESTDIR)$(PREFIX)/include" +	cp -- libar2.a "$(DESTDIR)$(PREFIX)/lib/" +	cp -- libar2.$(LIBEXT) "$(DESTDIR)$(PREFIX)/lib/libar2.$(LIBMINOREXT)" +	ln -sf -- libar2.$(LIBMINOREXT) "$(DESTDIR)$(PREFIX)/lib/libar2.$(LIBMAJOREXT)" +	ln -sf -- libar2.$(LIBMAJOREXT) "$(DESTDIR)$(PREFIX)/lib/libar2.$(LIBEXT)" +	cp -- libar2.h "$(DESTDIR)$(PREFIX)/include/" + +uninstall: +	-rm -f -- "$(DESTDIR)$(PREFIX)/lib/libar2.a" +	-rm -f -- "$(DESTDIR)$(PREFIX)/lib/libar2.$(LIBMAJOREXT)" +	-rm -f -- "$(DESTDIR)$(PREFIX)/lib/libar2.$(LIBMINOREXT)" +	-rm -f -- "$(DESTDIR)$(PREFIX)/lib/libar2.$(LIBEXT)" +	-rm -f -- "$(DESTDIR)$(PREFIX)/include/libar2.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..dd6f6c1 --- /dev/null +++ b/common.h @@ -0,0 +1,64 @@ +/* See LICENSE file for copyright and license details. */ +#include "libar2.h" + +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> + +#include <libblake.h> + + +#ifndef UINT_LEAST32_C +# ifdef UINT32_C +#  define UINT_LEAST32_C(V) UINT32_C(V) +# else +#  define UINT_LEAST32_C(V) V##UL +# endif +#endif + +#ifndef UINT_LEAST64_C +# ifdef UINT64_C +#  define UINT_LEAST64_C(V) UINT64_C(V) +# else +#  define UINT_LEAST64_C(V) V##ULL +# endif +#endif + + +#ifndef CACHE_LINE_SIZE +# define CACHE_LINE_SIZE 256 /* better with larger than actual than smaller than actual */ +#endif + + +#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 + +#define ELEMSOF(ARR) (sizeof(ARR) / sizeof(*(ARR))) + +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MIN(A, B) ((A) < (B) ? (A) : (B)) + + +#define ERASE(PTR, N) libar2_earse(PTR, N) +#define ERASE_ARRAY(ARR) ERASE(ARR, sizeof(ARR)) +#define ERASE_STRUCT(S) ERASE(&(S), sizeof(S)) + + +struct block { +	uint_least64_t w[1024 / (64 / 8)]; +}; diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..3a09d91 --- /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=c11 -Wall -O3 +LDFLAGS  = -s diff --git a/libar2.h b/libar2.h new file mode 100644 index 0000000..07e60eb --- /dev/null +++ b/libar2.h @@ -0,0 +1,618 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef LIBAR2_H +#define LIBAR2_H + +#include <stddef.h> +#include <stdint.h> + +/* for internal use { */ + +#if defined(UINT_LEAST32_C) +# define LIBAR2_UINT_LEAST32_C__(V) UINT_LEAST32_C(V) +#elif defined(UINT32_C) +# define LIBAR2_UINT_LEAST32_C__(V) UINT32_C(V) +#else +# define LIBAR2_UINT_LEAST32_C__(V) V##UL +#endif + +#define LIBAR2_MIN_T_COST LIBAR2_UINT_LEAST32_C__(1) +#define LIBAR2_MAX_T_COST LIBAR2_UINT_LEAST32_C__(0xFFFFffff) +#define LIBAR2_MIN_M_COST LIBAR2_UINT_LEAST32_C__(8) +#define LIBAR2_MAX_M_COST ((uint_least32_t)((SIZE_MAX >> 11) & LIBAR2_UINT_LEAST32_C__(0xFFFFffff))) +#define LIBAR2_MIN_LANES LIBAR2_UINT_LEAST32_C__(1) +#define LIBAR2_MAX_LANES LIBAR2_UINT_LEAST32_C__(0xFFFFff) +#define LIBAR2_MIN_SALTLEN ((size_t)LIBAR2_UINT_LEAST32_C__(8)) +#define LIBAR2_MAX_SALTLEN ((size_t)LIBAR2_UINT_LEAST32_C__(0xFFFFffff)) +#define LIBAR2_MAX_KEYLEN ((size_t)LIBAR2_UINT_LEAST32_C__(0xFFFFffff)) +#define LIBAR2_MAX_ADLEN ((size_t)LIBAR2_UINT_LEAST32_C__(0xFFFFffff)) +#define LIBAR2_MIN_HASHLEN ((size_t)LIBAR2_UINT_LEAST32_C__(4)) +#define LIBAR2_MAX_HASHLEN ((size_t)LIBAR2_UINT_LEAST32_C__(0xFFFFffff)) +#define LIBAR2_IS_TYPE_OK(T) ((T) == LIBAR2_ARGON2D || (T) == LIBAR2_ARGON2I || (T) == LIBAR2_ARGON2ID || (T) == LIBAR2_ARGON2DS) +#define LIBAR2_IS_VERSION_OK(V) ((V) == LIBAR2_ARGON2_VERSION_10 || (V) == LIBAR2_ARGON2_VERSION_13) + +/* } */ + +/** + * List all parameter errors + *  + * @param  X  Macro to expand for each error + * @param  P  A `const struct libar2_argon2_parameters *` to inspect + */ +#define LIBAR2_LIST_PARAMETER_ERRORS(X, P)\ +	X(LIBAR2_T_COST_TOO_SMALL, "time-cost parameter is too small", (P)->t_cost < LIBAR2_MIN_T_COST)\ +	X(LIBAR2_T_COST_TOO_LARGE, "time-cost parameter is too large", (P)->t_cost > LIBAR2_MAX_T_COST)\ +	X(LIBAR2_M_COST_TOO_SMALL, "memory-cost parameter is too small", (P)->m_cost < LIBAR2_MIN_M_COST)\ +	X(LIBAR2_M_COST_TOO_LARGE, "memory-cost parameter is too large", (P)->m_cost > LIBAR2_MAX_M_COST)\ +	X(LIBAR2_TOO_FEW_LANES, "lane-count parameter is too small", (P)->lanes < LIBAR2_MIN_LANES)\ +	X(LIBAR2_TOO_MANY_LANES, "lane-count parameter is too large", (P)->lanes > LIBAR2_MAX_LANES)\ +	X(LIBAR2_SALT_TOO_SMALL, "salt parameter is too small", (P)->saltlen < LIBAR2_MIN_SALTLEN)\ +	X(LIBAR2_SALT_TOO_LARGE, "salt parameter is too large", (P)->saltlen > LIBAR2_MAX_SALTLEN)\ +	X(LIBAR2_KEY_TOO_LARGE, "secret parameter is too large", (P)->keylen > LIBAR2_MAX_KEYLEN)\ +	X(LIBAR2_AD_TOO_LARGE, "associated data parameter is too large", (P)->adlen > LIBAR2_MAX_ADLEN)\ +	X(LIBAR2_HASH_TOO_SMALL, "tag length parameter is too small", (P)->hashlen < LIBAR2_MIN_HASHLEN)\ +	X(LIBAR2_HASH_TOO_LARGE, "tag length parameter is too large", (P)->hashlen > LIBAR2_MAX_HASHLEN)\ +	X(LIBAR2_INVALID_TYPE, "type parameter is invalid", !LIBAR2_IS_TYPE_OK((P)->type))\ +	X(LIBAR2_INVALID_VERSION, "version parameter is invalid", !LIBAR2_IS_VERSION_OK((P)->version)) + +/** + * Parameter errors + */ +enum libar2_parameter_error { + +	/** +	 * No error +	 */ +	LIBAR2_OK = 0 + +#define LIBAR2_X__(ENUM, ERRMESG, CONDITION) ,ENUM +	LIBAR2_LIST_PARAMETER_ERRORS(LIBAR2_X__,) +#undef LIBAR2_X__ +}; + +/** + * String case + */ +enum libar2_casing { + +	/** +	 * Lower case, e.g. "argon2i" +	 */ +	LIBAR2_LOWER_CASE = 0, + +	/** +	 * Title case, e.g. "Argon2i" +	 */ +	LIBAR2_TITLE_CASE = 1, + +	/** +	 * Upper case, e.g. "ARGON2I" +	 */ +	LIBAR2_UPPER_CASE = 2 +}; + +/** + * Argon2 primitive types + */ +enum libar2_argon2_type { + +	/** +	 * Secret-dependent hashing +	 *  +	 * Only for side-channel-free environment! +	 */ +	LIBAR2_ARGON2D = 0, + +	/** +	 * Secret-independent hashing +	 *  +	 * Good for side-channels but worse with respect +	 * to trade of attacks if only one pass is used +	 */ +	LIBAR2_ARGON2I = 1, + +	/** +	 * Hybrid construction +	 *  +	 * OK against side channels and better with +	 * respect to tradeoff attacks +	 */ +	LIBAR2_ARGON2ID = 2, + +	/* There is no type with value 3 */ + +	/** +	 * Substition box (S-box)-hardened +	 */ +	LIBAR2_ARGON2DS = 4 +}; + +/** + * Argon2 versions + */ +enum libar2_argon2_version { + +	/** +	 * Argon2 version 1.0 ("10") +	 */ +	LIBAR2_ARGON2_VERSION_10 = 0x10, + +	/** +	 * Argon2 version 1.3 ("13") +	 */ +	LIBAR2_ARGON2_VERSION_13 = 0x13 +}; + +/** + * Argon2 hashing parameters + */ +struct libar2_argon2_parameters { + +	/** +	 * Primitive type +	 */ +	enum libar2_argon2_type type; + +	/** +	 * Version number +	 *  +	 * `libar2_latest_argon2_version` is recommended +	 */ +	enum libar2_argon2_version version; + +	/** +	 * Number of passes +	 *  +	 * At least 1, at most 2³²−1 +	 */ +	uint_least32_t t_cost; + +	/** +	 * Amount of required memory, in kilobytes +	 *  +	 * At least 8, at most MAX(2³²−1, address-space » 11) +	 */ +	uint_least32_t m_cost; + +	/** +	 * Number of lanes +	 *  +	 * At least 1, at most 2²⁴−1 +	 */ +	uint_least32_t lanes; + +	/** +	 * Salt, binary +	 *  +	 * Only modified if `.autoerase_salt` in +	 * `struct libar2_context` is non-zero +	 */ +	unsigned char *salt; + +	/** +	 * The length (bytes) of the salt +	 *  +	 * At least 8, at most 2³²−1 +	 */ +	size_t saltlen; + +	/** +	 * Secret (pepper), binary [optional] +	 *  +	 * Only modified if `.autoerase_secret` in +	 * `struct libar2_context` is non-zero +	 */ +	unsigned char *key; + +	/** +	 * The length (bytes) of the secret +	 *  +	 * At least 0, at most 2³²−1 +	 */ +	size_t keylen; + +	/** +	 * Arbitrary extra associated data, binary [optional] +	 *  +	 * Only modified if `.autoerase_associated_data` +	 * in `struct libar2_context` is non-zero +	 */ +	unsigned char *ad; + +	/** +	 * The length (bytes) of the associated +	 *  +	 * At least 0, at most 2³²−1 +	 */ +	size_t adlen; + +	/** +	 * The length (bytes) of the output hash +	 *  +	 * At least 4, at most 2³²−1 +	 */ +	size_t hashlen; +}; + +/** + * Library settings + */ +struct libar2_context { + +	/** +	 * User-defined data +	 */ +	void *user_data; + +	/** +	 * Whether the message shall be erased +	 * immediately when it's no longer need +	 *  +	 * Assumming the message is a password, +	 * you would normally set this to non-zero +	 * (properly 1), unless the password is in +	 * read-only memory for will be needed for +	 * rehashing with a stronger algorithm or +	 * new parameters +	 */ +	unsigned char autoerase_message; + +	/** +	 * Whether the secret shall be erased +	 * immediately when it's no longer need +	 */ +	unsigned char autoerase_secret; + +	/** +	 * Whether the salt shall be erased +	 * immediately when it's no longer need +	 */ +	unsigned char autoerase_salt; + +	/** +	 * Whether the associated data shall be +	 * erased immediately when it's no longer +	 * need +	 */ +	unsigned char autoerase_associated_data; + +	/** +	 * Memory allocation function +	 *  +	 * It is safe to assume that `.allocate` and +	 * `.deallocate` will be called in stack order +	 * and never in threads run using `.run_thread` +	 *  +	 * Example implementation: +	 *  +	 *     static void * +	 *     allocate(size_t num, size_t size, size_t alignment, struct libar2_context *ctx) +	 *     { +	 *             void *ptr; +	 *             int err; +	 *             (void) ctx; +	 *             if (num > SIZE_MAX / size) { +	 *                     errno = ENOMEM; +	 *                     return NULL; +	 *             } +	 *             if (alignment < sizeof(void *)) +	 *                     alignment = sizeof(void *); +	 *             err = posix_memalign(&ptr, alignment, num * size); +	 *             if (err) { +	 *                     errno = err; +	 *                     return NULL; +	 *             } else { +	 *                     return ptr; +	 *             } +	 *     } +	 *  +	 * @param   num        The number of elements to allocate, never 0 +	 * @param   size       The size of each element, never 0 +	 * @param   alignment  Requires memory alignment, never 0 +	 * @param   ctx        The structure containing the callback +	 * @return             Pointer to the allocated memory, `NULL` on failure +	 */ +	void *(*allocate)(size_t num, size_t size, size_t alignment, struct libar2_context *ctx); + +	/** +	 * Memory deallocation function +	 *  +	 * The application may which to earse the memory before +	 * deallocating it; this is not done by the library. +	 * This can be done using `libar2_earse`; +	 *  +	 * Example implementation: +	 *  +	 *     static void +	 *     deallocate(void *ptr, struct libar2_context *ctx) +	 *     { +	 *             (void) ctx; +	 *             free(ptr); +	 *     } +	 *  +	 * @param  ptr  The pointer to the memory to deallocate, +	 *              always a pointer returned by `.allocate` +	 * @param  ctx  The structure containing the callback +	 */ +	void (*deallocate)(void *ptr, struct libar2_context *ctx); + +	/** +	 * Initialise the thread pool +	 *  +	 * If thread support is desired, but the application do not +	 * want to keep track of the threads using a thread pool, +	 * this function must store `desired` in `*createdp`. The +	 * application must also, in this case, make sure that +	 * `.join_thread_pool` returns after all started threads +	 * have stopped, and `.get_ready_threads` store unique +	 * indices within the range [0, `desired`) (start with +	 * `i = 0` and each time an index is stored, calculate it +	 * with `i++ % desired`). Alternatively, and more preferably, +	 * this scheme can be used, but adapted to limit the number +	 * of concurrent threads, keeping track of the number of +	 * running threads, and not let `.get_ready_threads` return +	 * before this number is small enough; `*createdp` must however +	 * still set to `desired` so that to threads are not running +	 * concurrently with the same memory segment as the provided +	 * argument for the function to run, as this could be a source +	 * of memory corruption. It is however recommended to implement +	 * proper thread pooling as the library will call `.run_thread` +	 * `4 * params->t_cost * params->lanes` times where `params` +	 * is the used `struct libar2_argon2_parameters *`. +	 *  +	 * @param   desired   The number of threads that, at a maximum, +	 *                    will be used by the library, from the +	 *                    thread pool +	 * @param   createdp  Output parameter for the number of threads +	 *                    allocated, 0 if threading is disabled, +	 *                    which is preferable if `desired` is 1 +	 * @param   ctx       The structure containing the callback +	 * @return            0 on success, -1 on failure (the calling +	 *                    function will exit indicating error and keep +	 *                    the value of `errno` set by this function) +	 */ +	int (*init_thread_pool)(size_t desired, size_t *createdp, struct libar2_context *ctx); + +	/** +	 * Wait until at least one thread in the pool is ready +	 * (may be immediately), and get some of their indices +	 *  +	 * @param   indices  Output array for the indices of the ready threads +	 * @param   n        The maximum number of thread indices to store in `indices`; +	 *                   this number may exceed the number of created, or even +	 *                   requested, threads, or the number of threads the function +	 *                   will attempt to use +	 * @param   ctx      The structure containing the callback +	 * @return           The number of ready threads (non-zero, but may exceed `n`); +	 *                   0 on failure (the calling function will exit indicating +	 *                   error and keep the value of `errno` set by this function) +	 */ +	size_t (*get_ready_threads)(size_t *indices, size_t n, struct libar2_context *ctx); + +	/** +	 * Run a function in a thread +	 *  +	 * The requested thread will be guaranteed by +	 * `.get_ready_threads` to be ready +	 *  +	 * @param   index     The index of the thread to use +	 * @param   function  The function to run +	 * @param   data      Argument to provide to `function` +	 * @param   ctx       The structure containing the callback +	 * @return            0 on success, -1 on failure (the calling +	 *                    function will exit indicating error and keep +	 *                    the value of `errno` set by this function) +	 */ +	int (*run_thread)(size_t index, void (*function)(void *data), void *data, struct libar2_context *ctx); + +	/** +	 * Wait until all threads in the thread pool are resting +	 *  +	 * @param   ctx  The structure containing the callback +	 * @return       0 on success, -1 on failure (the calling +	 *               function will exit indicating error and keep +	 *               the value of `errno` set by this function) +	 */ +	int (*join_thread_pool)(struct libar2_context *ctx); + +	/** +	 * Destroy the thread pool, and all threads in it +	 * (each of the will be resting) +	 *  +	 * Will be called iff `.init_thread_pool` was called +	 * successfully and returned a non-zero thread +	 * count, except, not if `.join_thread_pool` or +	 * `.get_ready_threads` failed +	 *  +	 * @param   ctx  The structure containing the callback +	 * @return       0 on success, -1 on failure (the calling +	 *               function will exit indicating error and keep +	 *               the value of `errno` set by this function) +	 */ +	int (*destroy_thread_pool)(struct libar2_context *ctx); +}; + + +/** + * The latest versions of Argon2 that is supported + */ +extern enum libar2_argon2_version libar2_latest_argon2_version; + + +/** + * Convert an Argon2 primitive type value to a string + *  + * @param   type    The primitive type + * @param   casing  The case that the string shalll use + * @return          String representing the type, `NULL` (with `errno` + *                  set to EINVAL) if either argument is invalid + */ +const char *libar2_type_to_string(enum libar2_argon2_type type, enum libar2_casing casing); + +/** + * Convert a string to an Argon2 primitive type value + *  + * @param   str    String representing the primitive type + * @param   typep  Output parameter for the primitive type + * @return         0 on success, -1 (with `errno` set to EINVAL) if `str` is invalid + */ +int libar2_string_to_type(const char *str, enum libar2_argon2_type *typep); + +/** + * Convert an Argon2 version number value to a string, + * will be returned without a dot + *  + * @param   version  The version number value + * @return           String representing the version, `NULL` (with + *                   `errno` set to EINVAL) if `version` is invalid + */ +const char *libar2_version_to_string(enum libar2_argon2_version version); + +/** + * Convert an Argon2 version number value to a string, + * will be returned with a dot + *  + * @param   version  The version number value + * @return           String representing the version, `NULL` (with + *                   `errno` set to EINVAL) if `version` is invalid + */ +const char *libar2_version_to_string_proper(enum libar2_argon2_version version); + +/** + * Convert a string to an Argon2 version number value + *  + * @param   str       String representing the version + * @param   versionp  Output parameter for the version number value + * @return            0 on success, -1 (with `errno` set to EINVAL) if `str` is invalid + */ +int libar2_string_to_version(const char *str, enum libar2_argon2_version *versionp); + +/** + * Encode hashing parameters + *  + * Secret, associated data, and tag length (`params->hashlen`) + * will not be included in the output + *  + * To encode a string with both the parameters and the + * hash, simply append the output of `libar2_encode_base64` + * over the hash onto the output of this function + *  + * @param   buf     Output buffer, or `NULL` + * @param   params  Hashing parameters + * @return          The number of bytes required for `buf`, + *                  including the NUL byte added to the end + */ +size_t libar2_encode_params(char *buf, const struct libar2_argon2_parameters *params); + +/** + * Encode data with base64 encoding, which is what + * Argon2 uses, rather than B64 which is what crypt(3) + * uses; however this version of base64 is not padded + * to a length divible by 4 + *  + * @param   buf   Output buffer, or `NULL` + * @param   data  The data to encode + * @param   len   The number of bytes in `data` + * @return        The number of bytes required for `buf`, + *                including the NUL byte added to the end + */ +size_t libar2_encode_base64(char *buf, const void *data, size_t len); + +/** + * Decode hashing parameters + *  + * Secret and associated data will be set to zero-length + *  + * It is recommended that application stores that default + * parameters encoded with `libar2_encode_params` using + * a hash of only null bytes. When the allocation decodes + * this string before generating the hash for a new password, + * it would refill the salt buffer with random bytes. The + * salt buffer will be allocated by this function. + *  + * The tag length (`params->hashlen`) will calculated from + * that hash at the end of `str`, however, the encoded length + * of the hash will not be included in the function's return + * value + *  + * @param   str     Hashing parameter string + * @param   params  Output parameter for the hashing parameters + * @param   bufp    Output parameter for buffer containing variable + *                  length data in `params`; will be allocated + *                  using `ctx->allocate` + * @param   ctx     `.allocate` and `.deallocate` must be set + * @return          The number of bytes read, 0 if `str` is improperly + *                  formatted (EINVAL), contain an unrecognised primitive + *                  type (EINVAL), or contained a value that is too large + *                  to be stored (ERANGE) (otherwise invalid parameters + *                  are not checked); if a callback from `ctx` fails, 0 + *                  is returned with `errno` set to the value set by the + *                  callback; if non-zero is returned, and `str` contains + *                  a hash, and not just parameters, `&str[return]` will + *                  point to the hash + */ +size_t libar2_decode_params(const char *str, struct libar2_argon2_parameters *params, char **bufp, struct libar2_context *ctx); + +/** + * Decode data encoded with base64 (padding with '=' is optional) + *  + * @param   str   The data to decode + * @param   data  Output buffer for the decoded data, or `NULL` + * @param   lenp  Output parameter for the length of the decoded data + * @return        The number of bytes read + */ +size_t libar2_decode_base64(const char *str, void *data, size_t *lenp); + +/** + * Validate hashing parameters + *  + * @param   params   The hashing parameters + * @param   errmsgp  Output parameter for the error message, or `null` + * @return           The first detected error, or LIBAR2_OK (0) if none + */ +enum libar2_parameter_error libar2_validate_params(const struct libar2_argon2_parameters *params, const char **errmsgp); + +/** + * Securily earse memory + *  + * @param  mem   The memory to erase + * @param  size  The number of bytes to erase + */ +void libar2_earse(volatile void *mem, size_t size); + +/** + * Hash a message + *  + * The recommended why of verify a password is to hash the + * provided password with this function, convert the known + * hash with `libar2_decode_base64` to binary (if it's not + * already in binary), and let `h` be the result of this + * function, `k` be the known password hash, and `n` be + * `params->hashlen`, then the password is correct iff + * `d`, as computed below, is 0: + *   + *      unsigned char d = 0; + *      size_t i; + *      for (i = 0; i < n; i++) { + *          d |= h[i] ^ k[i]; + *      } + *  + * It is preferable this check is done by the process that + * knowns the correct password hash, and that the tryed + * password is hashed before it is sent to that process + *  + * Note that on failure, the function will not necessarily + * have erased data configured in `ctx` to be automatically + * erased + *  + * @param   hash    Binary hash ("tag") output buffer, shall + *                  be at least `params->hashlen` bytes large + * @param   msg     Message (password) to hash; only modified if + *                  `ctx->autoerase_message` is non-zero + * @param   msglen  The length of `msg`; at least 0, at most 2³²−1 + * @param   params  Hashing parameters + * @param   ctx     Library settings + * @return          0 on success, -1 on failure + */ +int libar2_hash(void *hash, void *msg, size_t msglen, struct libar2_argon2_parameters *params, struct libar2_context *ctx); + +#endif diff --git a/libar2_decode_base64.c b/libar2_decode_base64.c new file mode 100644 index 0000000..df35a60 --- /dev/null +++ b/libar2_decode_base64.c @@ -0,0 +1,68 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + +#define FF 0xFF +static const unsigned char lut[256] = { +	FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, +	FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, +	FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 62, FF, FF, FF, 63, +	52, 53, 54, 55, 56, 57, 58, 59, 60, 61, FF, FF, FF, FF, FF, FF, +	FF,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, +	15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, FF, FF, FF, FF, FF, +	FF, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, +	41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, FF, FF, FF, FF, FF, +	FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, +	FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, +	FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, +	FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, +	FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, +	FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, +	FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, +	FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF +}; + + +size_t +libar2_decode_base64(const char *str_, void *data_, size_t *lenp) +{ +	const unsigned char *str = (const unsigned char *)str_; +	unsigned char *data = data_; +	unsigned char a, b, c, d; +	size_t ret = 0; + +	*lenp = 0; + +	for(;; str += 4) { +		if (lut[str[0]] == FF || lut[str[1]] == FF) +			break; +		a = lut[str[0]]; +		b = lut[str[1]]; +		ret += 2; +		if (data) +			*data++ = (unsigned char)((a << 2) | (b >> 4)); +		++*lenp; + +		if (lut[str[2]] == FF) { +			ret += (str[2] == '='); +			ret += (str[2] == '=' && str[3] == '='); +			break; +		} +		c = lut[str[2]]; +		ret += 1; +		if (data) +			*data++ = (unsigned char)((b << 4) | (c >> 2)); +		++*lenp; + +		if (lut[str[3]] == FF) { +			ret += (str[3] == '='); +			break; +		} +		d = lut[str[3]]; +		ret += 1; +		if (data) +			*data++ = (unsigned char)((c << 6) | (d >> 0)); +		++*lenp; +	} + +	return ret; +} diff --git a/libar2_decode_params.c b/libar2_decode_params.c new file mode 100644 index 0000000..cc90c24 --- /dev/null +++ b/libar2_decode_params.c @@ -0,0 +1,147 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.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_C(0xFFFFffff) - digit) / 10) { +			errno = ERANGE; +			return 0; +		} +		*outp = *outp * 10 + digit; +	} + +	return i; +} + + +size_t +libar2_decode_params(const char *str, struct libar2_argon2_parameters *params, char **bufp, struct libar2_context *ctx) +{ +	const char *start = str; +	uint_least32_t u32, *u32p; +	int have_t = 0, have_m = 0, have_p = 0; +	size_t n, q, r; + +	*bufp = NULL; +	params->salt = NULL; +	params->saltlen = 0; +	params->key = NULL; +	params->keylen = 0; +	params->ad = NULL; +	params->adlen = 0; + +	if (*str++ != '$') +		goto einval; + +	if (libar2_string_to_type(str, ¶ms->type)) +		goto fail; +	while (*str && *str != '$') +		str++; + +	if (*str++ != '$') +		goto einval; + +	if (str[0] == 'v' && str[1] == '=') { +		n = decode_u32(&str[2], &u32); +		if (!n) +			goto fail; +		if (u32 > (uint_least32_t)INT_MAX) +			goto erange; +		params->version = (enum libar2_argon2_version)u32; +		str += n + 2; +		if (*str++ != '$') +			goto einval; +	} else { +		params->version = LIBAR2_ARGON2_VERSION_10; +	} + +	while (*str && *str != '$') { +		if (str[0] == 't' && str[1] == '=') { +			if (have_t) +				goto einval; +			have_t = 1; +			u32p = ¶ms->t_cost; +			str += 2; + +		} else if (str[0] == 'm' && str[1] == '=') { +			if (have_m) +				goto einval; +			have_m = 1; +			u32p = ¶ms->m_cost; +			str += 2; + +		} else if (str[0] == 'p' && str[1] == '=') { +			if (have_p) +				goto einval; +			have_p = 1; +			u32p = ¶ms->lanes; +			str += 2; + +		} else { +			goto einval; +		} + +		n = decode_u32(str, u32p); +		if (!n) +			goto fail; +		str += n; +		if (*str == '$') +			break; +		if (*str != ',') +			goto einval; +		str++; +	} + +	if (have_t + have_m + have_p != 3) +		goto einval; + +	if (*str++ != '$') +		goto einval; + +	n = libar2_decode_base64(str, NULL, ¶ms->saltlen); +	if (params->saltlen) { +		*bufp = ctx->allocate(params->saltlen, sizeof(char), ALIGNOF(char), ctx); +		if (!*bufp) +			goto fail; +	} +	str += libar2_decode_base64(str, *bufp, ¶ms->saltlen); +	params->salt = (void *)*bufp; + +	if (*str++ != '$') +		goto einval; + +	params->hashlen = 0; +	while (isalnum(str[params->hashlen]) || str[params->hashlen] == '+' || str[params->hashlen] == '/') +		params->hashlen += 1; +	q = params->hashlen / 4; +	r = params->hashlen % 4; +	params->hashlen = q * 3 + (r == 3 ? 2 : r == 2 ? 1 : 0); + +	return (size_t)(str - start); + +einval: +	errno = EINVAL; +	goto fail; + +erange: +	errno = ERANGE; +fail: +	if (*bufp) { +		ctx->deallocate(*bufp, ctx); +		*bufp = NULL; +	} +	return 0; +} diff --git a/libar2_earse.c b/libar2_earse.c new file mode 100644 index 0000000..905695a --- /dev/null +++ b/libar2_earse.c @@ -0,0 +1,38 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +#if defined(memset_s) +#elif defined(explicit_bzero) || defined(__OpenBSD__) +#elif defined(explicit_memset) +#else +# if defined(__GNUC__) +__attribute__((visibility("hidden"))) +# endif +void *(*const volatile libar2_internal_explicit_memset__)(void *, int, size_t) = &memset; +#endif + + +#if defined(__clang__) /* before __GNUC__ because that is also set in clang */ +# if __has_attribute(optnone) +__attribute__((optnone)) +# endif +#elif defined(__GNUC__) +# if __GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ >= 40400 +__attribute__((optimize("O0"))) +# endif +#endif +void +libar2_earse(volatile void *mem_, size_t size) +{ +	void *mem = *(void **)(void *)&mem_; +#if defined(memset_s) +	memset_s(mem, size); +#elif defined(explicit_bzero) || defined(__OpenBSD__) +	explicit_bzero(mem, size); +#elif defined(explicit_memset) +	explicit_memset(mem, 0, size); +#else +	libar2_internal_explicit_memset__(mem, 0, size); +#endif +} diff --git a/libar2_encode_base64.c b/libar2_encode_base64.c new file mode 100644 index 0000000..857b3e8 --- /dev/null +++ b/libar2_encode_base64.c @@ -0,0 +1,42 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +#define ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" +static char lut[256] = ALPHABET ALPHABET ALPHABET ALPHABET; + +#define O1(I1, I2, I3) ((I1) >> 2) +#define O2(I1, I2, I3) (((I1) << 4) | ((I2) >> 4)) +#define O3(I1, I2, I3) (((I2) << 2) | ((I3) >> 6)) +#define O4(I1, I2, I3) (I3) + + +size_t +libar2_encode_base64(char *buf, const void *data_, size_t len) +{ +	const unsigned char *data = data_; +	size_t q, r, i; + +	q = len / 3; +	r = len % 3; + +	if (buf) { +		for (i = 0; i < q; i++, data += 3) { +			*buf++ = lut[O1(data[0], data[1], data[2]) & 255]; +	                *buf++ = lut[O2(data[0], data[1], data[2]) & 255]; +			*buf++ = lut[O3(data[0], data[1], data[2]) & 255]; +	                *buf++ = lut[O4(data[0], data[1], data[2]) & 255]; +		} +		if (r == 1) { +			*buf++ = lut[O1(data[0], 0, 0) & 255]; +	                *buf++ = lut[O2(data[0], 0, 0) & 255]; +		} else if (r == 2) { +			*buf++ = lut[O1(data[0], data[1], 0) & 255]; +	                *buf++ = lut[O2(data[0], data[1], 0) & 255]; +			*buf++ = lut[O3(data[0], data[1], 0) & 255]; +		} +		*buf = '\0'; +	} + +	return (q * 4) + r + !!r + 1; +} diff --git a/libar2_encode_params.c b/libar2_encode_params.c new file mode 100644 index 0000000..cdf634a --- /dev/null +++ b/libar2_encode_params.c @@ -0,0 +1,31 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +size_t +libar2_encode_params(char *buf, const struct libar2_argon2_parameters *params) +{ +	size_t off; + +#define FMT_AND_ARGS\ +	"$%s$v=%i$m=%ju,t=%ju,p=%ju$",\ +	libar2_type_to_string(params->type, LIBAR2_LOWER_CASE),\ +	(int)params->version,\ +	(uintmax_t)params->m_cost,\ +	(uintmax_t)params->t_cost,\ +	(uintmax_t)params->lanes + +	if (buf) { +		off = (size_t)sprintf(buf, FMT_AND_ARGS); +		off += libar2_encode_base64(&buf[off], params->salt, params->saltlen) - 1; +		buf[off++] = '$'; +		buf[off++] = '\0'; + +	} else { +		off = (size_t)snprintf(NULL, 0, FMT_AND_ARGS); +		off += libar2_encode_base64(NULL, params->salt, params->saltlen) - 1; +		off += 2; +	} + +	return off; +} diff --git a/libar2_hash.c b/libar2_hash.c new file mode 100644 index 0000000..0eb9211 --- /dev/null +++ b/libar2_hash.c @@ -0,0 +1,612 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +struct threaded_fill_segments_params { +	struct block *memory; +	const uint_least64_t *sbox; +	struct libar2_argon2_parameters *params; +	uint_least32_t seglen; +	uint_least32_t lanelen; +	uint_least32_t blocks; +	uint_least32_t pass; +	uint_least32_t lane; +	uint_least32_t slice; +}; + + +static const struct libblake_blake2b_params b2params = { +	.digest_len = 64, +	.key_len = 0, +	.fanout = 1, +	.depth = 1, +	.leaf_len = 0, +	.node_offset = 0, +	.node_depth = 0, +	.inner_len = 0 +}; + + +static const struct block zerob; /* implicitly zeroed via `static` */ + + +static void +memxor(void *a_, const void *b_, size_t n) +{ +	unsigned char *a = a_; +	const unsigned char *b = b_; +	size_t i; +	for (i = 0; i < n; i++) +		a[i] ^= b[i]; +} + + +static size_t +store32(unsigned char *out, uint_least32_t value) +{ +	out[0] = (unsigned char)((value >> 0) & 255); +	out[1] = (unsigned char)((value >> 8) & 255); +	out[2] = (unsigned char)((value >> 16) & 255); +	out[3] = (unsigned char)((value >> 24) & 255); +	return 4; +} + + +static void +store64(unsigned char *out, uint_least64_t value) +{ +	out[0] = (unsigned char)((value >> 0) & 255); +	out[1] = (unsigned char)((value >> 8) & 255); +	out[2] = (unsigned char)((value >> 16) & 255); +	out[3] = (unsigned char)((value >> 24) & 255); +	out[4] = (unsigned char)((value >> 32) & 255); +	out[5] = (unsigned char)((value >> 40) & 255); +	out[6] = (unsigned char)((value >> 48) & 255); +	out[7] = (unsigned char)((value >> 56) & 255); +} + + +static void +load64(uint_least64_t *out, const unsigned char *data) +{ +	*out = ((uint_least64_t)(data[0] & 255) << 0) +	     | ((uint_least64_t)(data[1] & 255) << 8) +	     | ((uint_least64_t)(data[2] & 255) << 16) +	     | ((uint_least64_t)(data[3] & 255) << 24) +	     | ((uint_least64_t)(data[4] & 255) << 32) +	     | ((uint_least64_t)(data[5] & 255) << 40) +	     | ((uint_least64_t)(data[6] & 255) << 48) +	     | ((uint_least64_t)(data[7] & 255) << 56); +} + + +static void +store_block(unsigned char *block8, const struct block *block64) +{ +	size_t i, j; +	for (i = 0, j = 0; i < 1024; i += 8, j += 1) +		store64(&block8[i], block64->w[j]); +} + + +static void +load_block(struct block *block64, const unsigned char *block8) +{ +	size_t i, j; +	for (i = 0, j = 0; i < 1024; i += 8, j += 1) +		load64(&block64->w[j], &block8[i]); +} + + +static size_t +storemem(unsigned char *out, const void *mem, size_t len, size_t max) +{ +	size_t n = MIN(len, max); +	memcpy(out, mem, n); +	return n; +} + + +static uint_least64_t +rotr64(uint_least64_t x, int n) +{ +	return ((x >> n) | (x << (64 - n))) & UINT_LEAST64_C(0xFFFFffffFFFFffff); +} + + +static uint_least64_t +fBlaMka(uint_least64_t x, uint_least64_t y) +{ +	return x + y + 2 * (x & UINT_LEAST64_C(0xFFffFFff)) * (y & UINT_LEAST64_C(0xFFffFFff)); +} + + +static void +fill_block(struct block *block, const struct block *prevblock, const struct block *refblock, +           int with_xor, const uint_least64_t *sbox) +{ +	uint_least64_t x = 0; +	uint_least32_t x_hi, x_lo; +	struct block tmpblock; +	size_t i; + +	if (with_xor) { +		for (i = 0; i < ELEMSOF(refblock->w); i++) +			block->w[i] ^= tmpblock.w[i] = refblock->w[i] ^ prevblock->w[i]; +	} else { +		for (i = 0; i < ELEMSOF(refblock->w); i++) +			block->w[i] = tmpblock.w[i] = refblock->w[i] ^ prevblock->w[i]; +	} + +	if (sbox) { +		x = tmpblock.w[0] ^ tmpblock.w[ELEMSOF(tmpblock.w) - 1]; +		for (i = 0; i < 96; i++) { +			x_hi = (uint_least32_t)(x >> 32); +			x_lo = (uint_least32_t)x & UINT_LEAST32_C(0xFFFFffff); +			x = (uint_least64_t)x_hi * (uint_least64_t)x_lo; +			x += sbox[(x_hi & UINT_LEAST32_C(0x1FF)) + 0]; +			x ^= sbox[(x_lo & UINT_LEAST32_C(0x1FF)) + 512]; +		} +	} + +#define BLAMKA_G(A, B, C, D)\ +	A = fBlaMka(A, B);\ +	D = rotr64(D ^ A, 32);\ +	C = fBlaMka(C, D);\ +	B = rotr64(B ^ C, 24);\ +	A = fBlaMka(A, B);\ +	D = rotr64(D ^ A, 16);\ +	C = fBlaMka(C, D);\ +	B = rotr64(B ^ C, 63) + +#define BLAMKA_ROUND(W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, WA, WB, WC, WD, WE, WF)\ +	BLAMKA_G(W0, W4, W8, WC);\ +	BLAMKA_G(W1, W5, W9, WD);\ +	BLAMKA_G(W2, W6, WA, WE);\ +	BLAMKA_G(W3, W7, WB, WF);\ +	BLAMKA_G(W0, W5, WA, WF);\ +	BLAMKA_G(W1, W6, WB, WC);\ +	BLAMKA_G(W2, W7, W8, WD);\ +	BLAMKA_G(W3, W4, W9, WE) + +#define BLAMKA_ROUND_(ARR, OFF, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, WA, WB, WC, WD, WE, WF)\ +	BLAMKA_ROUND(ARR[OFF + W0], ARR[OFF + W1], ARR[OFF + W2], ARR[OFF + W3],\ +	             ARR[OFF + W4], ARR[OFF + W5], ARR[OFF + W6], ARR[OFF + W7],\ +	             ARR[OFF + W8], ARR[OFF + W9], ARR[OFF + WA], ARR[OFF + WB],\ +	             ARR[OFF + WC], ARR[OFF + WD], ARR[OFF + WE], ARR[OFF + WF]) + +	for (i = 0; i < 8; i++) { +		BLAMKA_ROUND_(tmpblock.w, i * 16, +		               0,  1,  2,  3, +		               4,  5,  6,  7, +		               8,  9, 10, 11, +		              12, 13, 14, 15); +	} +	for (i = 0; i < 8; i++) { +		BLAMKA_ROUND_(tmpblock.w, i * 2, +		               0,  1, 16, 17, +		              32, 33, 48, 49, +		              64, 65, 80, 81, +		              96, 97, 112, 113); +	} + +	for (i = 0; i < ELEMSOF(refblock->w); i++) +		block->w[i] ^= tmpblock.w[i]; + +	block->w[0] += x; +	block->w[ELEMSOF(block->w) - 1] += x; +	block->w[0] &= UINT_LEAST64_C(0xFFFFffffFFFFffff); +	block->w[ELEMSOF(block->w) - 1] &= UINT_LEAST64_C(0xFFFFffffFFFFffff); +} + + +static void +generate_sbox(uint_least64_t *sbox, struct block *memory) +{ +	void *next, *prev = memory; +	size_t i; + +	for (i = 0; i < 8; i++) { +		next = &sbox[i * 128]; +		fill_block(next, &zerob, prev, 0, NULL); +		fill_block(next, &zerob, next, 0, NULL); +		prev = next; +	} +} + + +static void +next_address_block(struct block *addrb, struct block *inputb) +{ +	inputb->w[6] += 1; +	fill_block(addrb, &zerob, inputb, 0, NULL); +	fill_block(addrb, &zerob, addrb, 0, NULL); +} + + +static uint_least32_t +get_rindex(uint_least32_t seglen, uint_least32_t lanelen, uint_least32_t pass, +           uint_least32_t slice, uint_least32_t index, uint_least64_t prand, int same_lane) +{ +	uint_least32_t size, startpos; +	uint_least64_t relpos; + +	if (!pass) { +		if (!slice) +			size = index - 1; +		else if (same_lane) +			size = slice * seglen + index - 1; +		else +			size = slice * seglen - !index; +	} else { +		if (same_lane) +			size = lanelen - seglen + index - 1; +		else +			size = lanelen - seglen - !index; +	} + +	prand &= UINT_LEAST64_C(0xFFffFFff); +	relpos = (prand * prand) >> 32; +	relpos = ((uint_least64_t)size * relpos) >> 32; +	relpos = (uint_least64_t)size - 1 - relpos; + +	startpos = pass ? slice == 3 ? 0 : (slice + 1) * seglen : 0; + +	return (startpos + (uint_least32_t)relpos) % lanelen; +} + + +static void +fill_segment(struct block *memory, const uint_least64_t *sbox, struct libar2_argon2_parameters *params, +             uint_least32_t seglen, uint_least32_t lanelen, uint_least32_t blocks, +	     uint_least32_t pass, uint_least32_t lane, uint_least32_t slice) +{ +	int data_independent; +	struct block inputb, addrb; +	uint_least32_t off, prevoff, rlane, rindex; +	uint_least32_t index = 0, i; +	uint_least64_t prand; + +	data_independent = +		(params->type == LIBAR2_ARGON2I) || +		(params->type == LIBAR2_ARGON2ID && !pass && slice < 2); + +	if (data_independent) { +		memset(&inputb.w[6], 0, sizeof(*inputb.w) * (ELEMSOF(inputb.w) - 6)); +		inputb.w[0] = pass; +		inputb.w[1] = lane; +		inputb.w[2] = slice; +		inputb.w[3] = blocks; +		inputb.w[4] = params->t_cost; +		inputb.w[5] = (uint_least32_t)params->type; +	} + +	if (!pass && !slice) { +		if (data_independent) { +			next_address_block(&addrb, &inputb); +		} +		index = 2; +	} + +	off = lane * lanelen + slice * seglen + index; +	prevoff = off - 1 + (off % lanelen ? 0 : lanelen); + +	for (; index < seglen; index++, off++, prevoff++) { +		if (off % lanelen == 1) +			prevoff = off - 1; +		if (data_independent) { +			i = index % ELEMSOF(addrb.w); +			if (!i) { +				next_address_block(&addrb, &inputb); +			} +			prand = addrb.w[i]; +		} else { +			prand = memory[prevoff].w[0]; +		} + +		rlane = (!pass && !slice) ? lane : (uint_least32_t)(prand >> 32) % params->lanes; +		rindex = get_rindex(seglen, lanelen, pass, slice, index, prand, rlane == lane); + +		fill_block(&memory[off], &memory[prevoff], &memory[rlane * lanelen + rindex], +		           params->version > LIBAR2_ARGON2_VERSION_10 && pass, sbox); +	} +} + + +static void +threaded_fill_segment(void *data) +{ +	struct threaded_fill_segments_params *tparams = data; +	fill_segment(tparams->memory, tparams->sbox, tparams->params, +	             tparams->seglen, tparams->lanelen, tparams->blocks, +	             tparams->pass, tparams->lane, tparams->slice); +} + + +static void +initial_hash(unsigned char hash[static 64], void *msg, size_t msglen, +             struct libar2_argon2_parameters *params, struct libar2_context *ctx) +{ +#define SEGMENT(DATA, LEN, OFF) &((const unsigned char *)(DATA))[(OFF)], (LEN) - (OFF) + +	struct libblake_blake2b_state state; +	unsigned char block[128 + 3]; +	size_t n = 0, off; + +	libblake_blake2b_init(&state, &b2params, NULL); + +	n += store32(&block[n], params->lanes); +	n += store32(&block[n], (uint_least32_t)params->hashlen); +	n += store32(&block[n], params->m_cost); +	n += store32(&block[n], params->t_cost); +	n += store32(&block[n], (uint_least32_t)params->version); +	n += store32(&block[n], (uint_least32_t)params->type); +	n += store32(&block[n], (uint_least32_t)msglen); +	if (msglen) { +		n += off = storemem(&block[n], msg, msglen, 128 - n); +		if (n == 128) { +			libblake_blake2b_force_update(&state, block, n); +			n = 0; +			if (off < msglen) { +				off += libblake_blake2b_force_update(&state, SEGMENT(msg, msglen, off)); +				memcpy(block, SEGMENT(msg, msglen, off)); +				n = msglen - off; +			} +		} +		if (ctx->autoerase_message) +			ERASE(msg, msglen); +	} + +	n += store32(&block[n], (uint_least32_t)params->saltlen); +	if (n >= 128) { +		n -= libblake_blake2b_force_update(&state, block, n); +		memcpy(block, &block[128], n); /* overlap is impossible */ +	} +	if (params->saltlen) { +		if (!n) +			off = 0; +		else +			n += off = storemem(&block[n], params->salt, params->saltlen, 128 - n); +		if (n == 128) { +			libblake_blake2b_force_update(&state, block, n); +			n = 0; +		} +		if (n == 0 && off < params->saltlen) { +			off += libblake_blake2b_force_update(&state, SEGMENT(params->salt, params->saltlen, off)); +			memcpy(block, SEGMENT(params->salt, params->saltlen, off)); +			n = params->saltlen - off; +		} +		if (ctx->autoerase_salt) +			ERASE(params->salt, params->saltlen); +	} + +	n += store32(&block[n], (uint_least32_t)params->keylen); +	if (n >= 128) { +		n -= libblake_blake2b_force_update(&state, block, n); +		memcpy(block, &block[128], n); /* overlap is impossible */ +	} +	if (params->keylen) { +		if (!n) +			off = 0; +		else +			n += off = storemem(&block[n], params->key, params->keylen, 128 - n); +		if (n == 128) { +			libblake_blake2b_force_update(&state, block, n); +			n = 0; +		} +		if (n == 0 && off < params->keylen) { +			off += libblake_blake2b_force_update(&state, SEGMENT(params->key, params->keylen, off)); +			memcpy(block, SEGMENT(params->key, params->keylen, off)); +			n = params->keylen - off; +		} +		if (ctx->autoerase_secret) +			ERASE(params->key, params->keylen); +	} + +	n += store32(&block[n], (uint_least32_t)params->adlen); +	if (n > 128 || (n == 128 && params->adlen)) { +		n -= libblake_blake2b_force_update(&state, block, n); +		memcpy(block, &block[128], n); /* overlap is impossible */ +	} +	if (params->adlen) { +		if (!n) +			off = 0; +		else +			n += off = storemem(&block[n], params->ad, params->adlen, 128 - n); +		if (off < params->adlen) { +			if (n == 128) { +				libblake_blake2b_force_update(&state, block, n); +				n = 0; +			} +			if (n == 0) { +				off += libblake_blake2b_update(&state, SEGMENT(params->ad, params->adlen, off)); +				if (params->adlen - off > 128) +					off += libblake_blake2b_force_update(&state, SEGMENT(params->ad, params->adlen, off)); +				memcpy(block, SEGMENT(params->ad, params->adlen, off)); +				n = params->adlen - off; +			} +		} +		if (ctx->autoerase_associated_data) +			ERASE(params->ad, params->adlen); +	} + +	libblake_blake2b_digest(&state, block, n, 0, 64, hash); + +	ERASE_ARRAY(block); +	ERASE_STRUCT(state); + +#undef SEGMENT +} + + +static void /* this is not BLAKE2Xb, but something Argon2-specific */ +argon2_blake2b_exthash(void *hash_, size_t hashlen, void *msg_, size_t msglen) +{ +	struct libblake_blake2b_params params; +	struct libblake_blake2b_state state; +	unsigned char *msg = msg_; +	unsigned char block[128]; +	unsigned char *hash = hash_; +	size_t n, off; + +	params = b2params; +	params.digest_len = (uint_least8_t)MIN(hashlen, (size_t)params.digest_len); + +	libblake_blake2b_init(&state, ¶ms, NULL); +	n = store32(block, (uint_least32_t)hashlen); +	n += off = storemem(&block[n], msg, msglen, 128 - n); +	if (off == msglen) { +		libblake_blake2b_digest(&state, block, n, 0, params.digest_len, hash); +	} else { +		libblake_blake2b_force_update(&state, block, 128); +		libblake_blake2b_digest(&state, &msg[off], msglen - off, 0, params.digest_len, hash); +	} + +	if (hashlen > 64) { +		hashlen -= 32; +		params.digest_len = 64; +		while (hashlen > 64) { +			libblake_blake2b_init(&state, ¶ms, NULL); +			libblake_blake2b_digest(&state, hash, 64, 0, 64, &hash[32]); +			hash += 32; +			hashlen -= 32; +		} +		params.digest_len = (uint_least8_t)hashlen; +		libblake_blake2b_init(&state, ¶ms, NULL); +		libblake_blake2b_digest(&state, hash, 64, 0, hashlen, &hash[32]); +	} + +	ERASE_STRUCT(state); +	ERASE_ARRAY(block); +} + + +int +libar2_hash(void *hash, void *msg, size_t msglen, struct libar2_argon2_parameters *params, struct libar2_context *ctx) +{ +	unsigned char block[1024 + 128], hash0[256]; +	uint_least32_t blocks, seglen, lanelen; +	struct block *memory; +	size_t i, p, s, nthreads, ts[16], ti, tn; +	struct threaded_fill_segments_params *tparams = NULL; +	uint_least64_t *sbox = NULL; /* This is 8K large (assuming support for uint64_t), so we allocate it dynamically */ + +	if (libar2_validate_params(params, NULL) || msglen >> 31 > 1) { +		errno = EINVAL; +		return -1; +	} + +	blocks = MAX(params->m_cost, 8 * params->lanes); +	seglen = blocks / (4 * params->lanes); +	blocks -= blocks % (4 * params->lanes); +	lanelen = seglen * 4; + +	memory = ctx->allocate(blocks, sizeof(struct block), MAX(ALIGNOF(struct block), CACHE_LINE_SIZE), ctx); +	if (!memory) +		return -1; + +	if (params->type == LIBAR2_ARGON2DS) { +		sbox = ctx->allocate(1024, sizeof(*sbox), ALIGNOF(uint_least64_t), ctx); +		if (!sbox) { +			ctx->deallocate(memory, ctx); +			return -1; +		} +	} + +	initial_hash(hash0, msg, msglen, params, ctx); +	for (i = 0; i < params->lanes; i++) { +		store32(&hash0[64], 0); +		store32(&hash0[68], (uint_least32_t)i); +		argon2_blake2b_exthash(block, 1024, hash0, 72); +		load_block(&memory[i * lanelen + 0], block); /* TODO this is a copy function on LE-machines */ + +		store32(&hash0[64], 1); +		argon2_blake2b_exthash(block, 1024, hash0, 72); +		load_block(&memory[i * lanelen + 1], block); +	} + +	ERASE_ARRAY(hash0); + +	if (ctx->init_thread_pool(params->lanes, &nthreads, ctx)) +		goto fail; +	if (nthreads == 1) { +		nthreads = 0; +		if (ctx->destroy_thread_pool(ctx)) +			goto fail; +	} + +	if (!nthreads) { +		for (p = 0; p < params->t_cost; p++) { +			if (sbox) +				generate_sbox(sbox, memory); +			for (s = 0; s < 4; s++) { +				for (i = 0; i < params->lanes; i++) { +					fill_segment(memory, sbox, params, seglen, lanelen, blocks, +					             (uint_least32_t)p, (uint_least32_t)i, (uint_least32_t)s); +				} +			} +		} + +	} else { +		tparams = ctx->allocate(nthreads, sizeof(*tparams), ALIGNOF(struct threaded_fill_segments_params), ctx); +		if (!tparams) { +			ctx->destroy_thread_pool(ctx); +			goto fail; +		} +		for (i = 0; i < nthreads; i++) { +			tparams[i].memory = memory; +			tparams[i].sbox = sbox; +			tparams[i].params = params; +			tparams[i].seglen = seglen; +			tparams[i].lanelen = lanelen; +			tparams[i].blocks = blocks; +		} + +		for (p = 0; p < params->t_cost; p++) { +			if (sbox) +				generate_sbox(sbox, memory); +			for (s = 0; s < 4; s++) { +				ti = tn = 0; +				for (i = 0; i < params->lanes; i++) { +					if (ti == tn) { +						tn = ctx->get_ready_threads(ts, ELEMSOF(ts), ctx); +						if (!tn) +							goto fail; +					} +					tparams[ti].pass = (uint_least32_t)p; +					tparams[ti].lane = (uint_least32_t)i; +					tparams[ti].slice = (uint_least32_t)s; +					if (ctx->run_thread(ts[ti], threaded_fill_segment, &tparams[ti], ctx)) +						goto fail; +					ti++; +				} +				if (ctx->join_thread_pool(ctx)) +					goto fail; +			} +		} + +		if (ctx->destroy_thread_pool(ctx)) +			goto fail; +		ctx->deallocate(tparams, ctx); +		tparams = NULL; +	} + +	for (i = 1; i < params->lanes; i++) +		memxor(&memory[lanelen - 1], &memory[i * lanelen + lanelen - 1], sizeof(*memory)); +	store_block(block, &memory[lanelen - 1]); +	argon2_blake2b_exthash(hash, params->hashlen, block, 1024); + +	ERASE_ARRAY(block); +	if (sbox) +		ctx->deallocate(sbox, ctx); +	ctx->deallocate(memory, ctx); +	return 0; + +fail: +	if (tparams) +		ctx->deallocate(tparams, ctx); +	if (sbox) +		ctx->deallocate(sbox, ctx); +	ctx->deallocate(memory, ctx); +	return -1; +} diff --git a/libar2_latest_argon2_version.c b/libar2_latest_argon2_version.c new file mode 100644 index 0000000..043441d --- /dev/null +++ b/libar2_latest_argon2_version.c @@ -0,0 +1,5 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +enum libar2_argon2_version libar2_latest_argon2_version = LIBAR2_ARGON2_VERSION_13; diff --git a/libar2_string_to_type.c b/libar2_string_to_type.c new file mode 100644 index 0000000..98db6a8 --- /dev/null +++ b/libar2_string_to_type.c @@ -0,0 +1,30 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libar2_string_to_type(const char *str, enum libar2_argon2_type *typep) +{ +#define STRSTARTS(A, B) (!strncmp(A, B, sizeof(B) - 1)) + +	if (!strcasecmp(str, "argon2d") || STRSTARTS(str, "argon2d$")) { +		*typep = LIBAR2_ARGON2D; +		return 0; + +	} else if (!strcasecmp(str, "argon2i") || STRSTARTS(str, "argon2i$")) { +		*typep = LIBAR2_ARGON2I; +		return 0; + +	} else if (!strcasecmp(str, "argon2id") || STRSTARTS(str, "argon2id$")) { +		*typep = LIBAR2_ARGON2ID; +		return 0; + +	} else if (!strcasecmp(str, "argon2ds") || STRSTARTS(str, "argon2ds$")) { +		*typep = LIBAR2_ARGON2DS; +		return 0; + +	} else { +		errno = EINVAL; +		return -1; +	} +} diff --git a/libar2_string_to_version.c b/libar2_string_to_version.c new file mode 100644 index 0000000..a1e6370 --- /dev/null +++ b/libar2_string_to_version.c @@ -0,0 +1,20 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libar2_string_to_version(const char *str, enum libar2_argon2_version *versionp) +{ +	if (!strcmp(str, "10") || !strcmp(str, "1.0")) { +		*versionp = LIBAR2_ARGON2_VERSION_10; +		return 0; + +	} else if (!strcmp(str, "13") || !strcmp(str, "1.3")) { +		*versionp = LIBAR2_ARGON2_VERSION_13; +		return 0; + +	} else { +		errno = EINVAL; +		return -1; +	} +} diff --git a/libar2_type_to_string.c b/libar2_type_to_string.c new file mode 100644 index 0000000..3092101 --- /dev/null +++ b/libar2_type_to_string.c @@ -0,0 +1,19 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +const char * +libar2_type_to_string(enum libar2_argon2_type type, enum libar2_casing casing) +{ +	static const char *strs[3][5] = { +		{"argon2d", "argon2i", "argon2id", NULL, "argon2ds"}, +		{"Argon2d", "Argon2i", "Argon2id", NULL, "Argon2ds"}, +		{"ARGON2D", "ARGON2I", "ARGON2ID", NULL, "ARGON2DS"} +	}; + +	if (type < 0 || casing < 0 || type >= ELEMSOF(*strs) || casing >= ELEMSOF(strs) || !strs[casing][type]) { +		errno = EINVAL; +		return NULL; +	} +	return strs[casing][type]; +} diff --git a/libar2_validate_params.c b/libar2_validate_params.c new file mode 100644 index 0000000..02d159a --- /dev/null +++ b/libar2_validate_params.c @@ -0,0 +1,20 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +enum libar2_parameter_error +libar2_validate_params(const struct libar2_argon2_parameters *params, const char **errmsgp) +{ +#define LIBAR2_X__(ENUM, ERRMESG, CONDITION)\ +	if (CONDITION) {\ +		if (errmsgp)\ +			*errmsgp = ERRMESG;\ +		return ENUM;\ +	} +	LIBAR2_LIST_PARAMETER_ERRORS(LIBAR2_X__, params); +#undef LIBAR2_X__ + +	if (errmsgp) +		*errmsgp = "OK"; +	return LIBAR2_OK; +} diff --git a/libar2_version_to_string.c b/libar2_version_to_string.c new file mode 100644 index 0000000..8783a4a --- /dev/null +++ b/libar2_version_to_string.c @@ -0,0 +1,18 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +const char * +libar2_version_to_string(enum libar2_argon2_version version) +{ +	if (version == LIBAR2_ARGON2_VERSION_10) { +		return "10"; + +	} else if (version == LIBAR2_ARGON2_VERSION_13) { +		return "13"; + +	} else { +		errno = EINVAL; +		return NULL; +	} +} diff --git a/libar2_version_to_string_proper.c b/libar2_version_to_string_proper.c new file mode 100644 index 0000000..109b135 --- /dev/null +++ b/libar2_version_to_string_proper.c @@ -0,0 +1,18 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +const char * +libar2_version_to_string_proper(enum libar2_argon2_version version) +{ +	if (version == LIBAR2_ARGON2_VERSION_10) { +		return "1.0"; + +	} else if (version == LIBAR2_ARGON2_VERSION_13) { +		return "1.3"; + +	} else { +		errno = EINVAL; +		return NULL; +	} +} 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,736 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#include <stdlib.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 void * +allocate(size_t num, size_t size, size_t alignment, struct libar2_context *ctx) +{ +	void *ptr; +	int err; +	(void) ctx; +	if (num > SIZE_MAX / size) { +		errno = ENOMEM; +		return NULL; +	} +	if (alignment < sizeof(void *)) +		alignment = sizeof(void *); +	err = posix_memalign(&ptr, alignment, num * size); +	if (err) { +		errno = err; +		return NULL; +	} else { +		return ptr; +	} +} + +static void +deallocate(void *ptr, struct libar2_context *ctx) +{ +	(void) ctx; +	free(ptr); +} + +static int +st_init_thread_pool(size_t desired, size_t *createdp, struct libar2_context *ctx) +{ +	(void) desired; +	(void) ctx; +	*createdp = 0; +	return 0; +} + +static struct libar2_context ctx_st = { +	.user_data = NULL, +	.autoerase_message = 1, +	.autoerase_secret = 1, +	.autoerase_salt = 1, +	.autoerase_associated_data = 1, +	.allocate = allocate, +	.deallocate = deallocate, +	.init_thread_pool = st_init_thread_pool, +	.get_ready_threads = NULL, +	.run_thread = NULL, +	.join_thread_pool = NULL, +	.destroy_thread_pool = NULL +}; + + +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_libar2_type_to_string(void) +{ +	errno = 0; + +	assert_streq(libar2_type_to_string(LIBAR2_ARGON2D, LIBAR2_LOWER_CASE), "argon2d"); +	assert_streq(libar2_type_to_string(LIBAR2_ARGON2D, LIBAR2_TITLE_CASE), "Argon2d"); +	assert_streq(libar2_type_to_string(LIBAR2_ARGON2D, LIBAR2_UPPER_CASE), "ARGON2D"); +	assert(errno == 0); + +	assert_streq(libar2_type_to_string(LIBAR2_ARGON2I, LIBAR2_LOWER_CASE), "argon2i"); +	assert_streq(libar2_type_to_string(LIBAR2_ARGON2I, LIBAR2_TITLE_CASE), "Argon2i"); +	assert_streq(libar2_type_to_string(LIBAR2_ARGON2I, LIBAR2_UPPER_CASE), "ARGON2I"); +	assert(errno == 0); + +	assert_streq(libar2_type_to_string(LIBAR2_ARGON2ID, LIBAR2_LOWER_CASE), "argon2id"); +	assert_streq(libar2_type_to_string(LIBAR2_ARGON2ID, LIBAR2_TITLE_CASE), "Argon2id"); +	assert_streq(libar2_type_to_string(LIBAR2_ARGON2ID, LIBAR2_UPPER_CASE), "ARGON2ID"); +	assert(errno == 0); + +	assert_streq(libar2_type_to_string(LIBAR2_ARGON2DS, LIBAR2_LOWER_CASE), "argon2ds"); +	assert_streq(libar2_type_to_string(LIBAR2_ARGON2DS, LIBAR2_TITLE_CASE), "Argon2ds"); +	assert_streq(libar2_type_to_string(LIBAR2_ARGON2DS, LIBAR2_UPPER_CASE), "ARGON2DS"); +	assert(errno == 0); + +	assert_streq(libar2_type_to_string(LIBAR2_ARGON2I, -1), NULL); +	assert(errno == EINVAL); +	errno = 0; +	assert_streq(libar2_type_to_string(LIBAR2_ARGON2I, 3), NULL); +	assert(errno == EINVAL); +	errno = 0; + +	assert_streq(libar2_type_to_string(3, LIBAR2_LOWER_CASE), NULL); +	assert(errno == EINVAL); +	errno = 0; +	assert_streq(libar2_type_to_string(3, LIBAR2_TITLE_CASE), NULL); +	assert(errno == EINVAL); +	errno = 0; +	assert_streq(libar2_type_to_string(3, LIBAR2_UPPER_CASE), NULL); +	assert(errno == EINVAL); +	errno = 0; + +	assert_streq(libar2_type_to_string(-1, LIBAR2_LOWER_CASE), NULL); +	assert(errno == EINVAL); +	errno = 0; +	assert_streq(libar2_type_to_string(-1, LIBAR2_TITLE_CASE), NULL); +	assert(errno == EINVAL); +	errno = 0; +	assert_streq(libar2_type_to_string(-1, LIBAR2_UPPER_CASE), NULL); +	assert(errno == EINVAL); +	errno = 0; + +	assert_streq(libar2_type_to_string(5, LIBAR2_LOWER_CASE), NULL); +	assert(errno == EINVAL); +	errno = 0; +	assert_streq(libar2_type_to_string(5, LIBAR2_TITLE_CASE), NULL); +	assert(errno == EINVAL); +	errno = 0; +	assert_streq(libar2_type_to_string(5, LIBAR2_UPPER_CASE), NULL); +	assert(errno == EINVAL); +	errno = 0; +} + + +static void +check_libar2_string_to_type(void) +{ +	enum libar2_argon2_type type; + +	errno = 0; + +	assert(!libar2_string_to_type("argon2i", &type) && type == LIBAR2_ARGON2I); +	assert(!libar2_string_to_type("Argon2i", &type) && type == LIBAR2_ARGON2I); +	assert(!libar2_string_to_type("ARgon2i", &type) && type == LIBAR2_ARGON2I); +	assert(!libar2_string_to_type("ARGon2i", &type) && type == LIBAR2_ARGON2I); +	assert(!libar2_string_to_type("ARGOn2i", &type) && type == LIBAR2_ARGON2I); +	assert(!libar2_string_to_type("ARGON2i", &type) && type == LIBAR2_ARGON2I); +	assert(!libar2_string_to_type("ARGON2I", &type) && type == LIBAR2_ARGON2I); +	assert(!libar2_string_to_type("aRGON2I", &type) && type == LIBAR2_ARGON2I); +	assert(!libar2_string_to_type("arGON2I", &type) && type == LIBAR2_ARGON2I); +	assert(!libar2_string_to_type("argON2I", &type) && type == LIBAR2_ARGON2I); +	assert(!libar2_string_to_type("argoN2I", &type) && type == LIBAR2_ARGON2I); +	assert(!libar2_string_to_type("argon2I", &type) && type == LIBAR2_ARGON2I); +	assert(!libar2_string_to_type("argon2i$x", &type) && type == LIBAR2_ARGON2I); +	assert(errno == 0); + +	assert(!libar2_string_to_type("argon2d", &type) && type == LIBAR2_ARGON2D); +	assert(!libar2_string_to_type("Argon2d", &type) && type == LIBAR2_ARGON2D); +	assert(!libar2_string_to_type("ARgon2d", &type) && type == LIBAR2_ARGON2D); +	assert(!libar2_string_to_type("ARGon2d", &type) && type == LIBAR2_ARGON2D); +	assert(!libar2_string_to_type("ARGOn2d", &type) && type == LIBAR2_ARGON2D); +	assert(!libar2_string_to_type("ARGON2d", &type) && type == LIBAR2_ARGON2D); +	assert(!libar2_string_to_type("ARGON2D", &type) && type == LIBAR2_ARGON2D); +	assert(!libar2_string_to_type("aRGON2D", &type) && type == LIBAR2_ARGON2D); +	assert(!libar2_string_to_type("arGON2D", &type) && type == LIBAR2_ARGON2D); +	assert(!libar2_string_to_type("argON2D", &type) && type == LIBAR2_ARGON2D); +	assert(!libar2_string_to_type("argoN2D", &type) && type == LIBAR2_ARGON2D); +	assert(!libar2_string_to_type("argon2D", &type) && type == LIBAR2_ARGON2D); +	assert(!libar2_string_to_type("argon2d$x", &type) && type == LIBAR2_ARGON2D); +	assert(errno == 0); + +	assert(!libar2_string_to_type("argon2id", &type) && type == LIBAR2_ARGON2ID); +	assert(!libar2_string_to_type("Argon2id", &type) && type == LIBAR2_ARGON2ID); +	assert(!libar2_string_to_type("ARgon2id", &type) && type == LIBAR2_ARGON2ID); +	assert(!libar2_string_to_type("ARGon2id", &type) && type == LIBAR2_ARGON2ID); +	assert(!libar2_string_to_type("ARGOn2id", &type) && type == LIBAR2_ARGON2ID); +	assert(!libar2_string_to_type("ARGON2id", &type) && type == LIBAR2_ARGON2ID); +	assert(!libar2_string_to_type("ARGON2Id", &type) && type == LIBAR2_ARGON2ID); +	assert(!libar2_string_to_type("ARGON2ID", &type) && type == LIBAR2_ARGON2ID); +	assert(!libar2_string_to_type("aRGON2ID", &type) && type == LIBAR2_ARGON2ID); +	assert(!libar2_string_to_type("arGON2ID", &type) && type == LIBAR2_ARGON2ID); +	assert(!libar2_string_to_type("argON2ID", &type) && type == LIBAR2_ARGON2ID); +	assert(!libar2_string_to_type("argoN2ID", &type) && type == LIBAR2_ARGON2ID); +	assert(!libar2_string_to_type("argon2ID", &type) && type == LIBAR2_ARGON2ID); +	assert(!libar2_string_to_type("argon2id$x", &type) && type == LIBAR2_ARGON2ID); +	assert(errno == 0); + +	assert(!libar2_string_to_type("argon2ds", &type) && type == LIBAR2_ARGON2DS); +	assert(!libar2_string_to_type("Argon2ds", &type) && type == LIBAR2_ARGON2DS); +	assert(!libar2_string_to_type("ARgon2ds", &type) && type == LIBAR2_ARGON2DS); +	assert(!libar2_string_to_type("ARGon2ds", &type) && type == LIBAR2_ARGON2DS); +	assert(!libar2_string_to_type("ARGOn2ds", &type) && type == LIBAR2_ARGON2DS); +	assert(!libar2_string_to_type("ARGON2ds", &type) && type == LIBAR2_ARGON2DS); +	assert(!libar2_string_to_type("ARGON2Ds", &type) && type == LIBAR2_ARGON2DS); +	assert(!libar2_string_to_type("ARGON2DS", &type) && type == LIBAR2_ARGON2DS); +	assert(!libar2_string_to_type("aRGON2DS", &type) && type == LIBAR2_ARGON2DS); +	assert(!libar2_string_to_type("arGON2DS", &type) && type == LIBAR2_ARGON2DS); +	assert(!libar2_string_to_type("argON2DS", &type) && type == LIBAR2_ARGON2DS); +	assert(!libar2_string_to_type("argoN2DS", &type) && type == LIBAR2_ARGON2DS); +	assert(!libar2_string_to_type("argon2DS", &type) && type == LIBAR2_ARGON2DS); +	assert(!libar2_string_to_type("argon2ds$x", &type) && type == LIBAR2_ARGON2DS); +	assert(errno == 0); + +	assert(libar2_string_to_type("argon2", &type) == -1); +	assert(errno == EINVAL); +	errno = 0; +	assert(libar2_string_to_type("argon2x", &type) == -1); +	assert(errno == EINVAL); +	errno = 0; +	assert(libar2_string_to_type("ARGON2", &type) == -1); +	assert(errno == EINVAL); +	errno = 0; +} + + +static void +check_libar2_version_to_string(void) +{ +	errno = 0; + +	assert_streq(libar2_version_to_string(LIBAR2_ARGON2_VERSION_10), "10"); +	assert(errno == 0); + +	assert_streq(libar2_version_to_string(LIBAR2_ARGON2_VERSION_13), "13"); +	assert(errno == 0); + +	assert_streq(libar2_version_to_string(0x10), "10"); +	assert(errno == 0); + +	assert_streq(libar2_version_to_string(0x13), "13"); +	assert(errno == 0); + +	assert_streq(libar2_version_to_string(0x11), NULL); +	assert(errno == EINVAL); +	errno = 0; + +	assert_streq(libar2_version_to_string(0x12), NULL); +	assert(errno == EINVAL); +	errno = 0; + +	assert_streq(libar2_version_to_string(0), NULL); +	assert(errno == EINVAL); +	errno = 0; +} + + +static void +check_libar2_version_to_string_proper(void) +{ +	errno = 0; + +	assert_streq(libar2_version_to_string_proper(LIBAR2_ARGON2_VERSION_10), "1.0"); +	assert(errno == 0); + +	assert_streq(libar2_version_to_string_proper(LIBAR2_ARGON2_VERSION_13), "1.3"); +	assert(errno == 0); + +	assert_streq(libar2_version_to_string_proper(0x10), "1.0"); +	assert(errno == 0); + +	assert_streq(libar2_version_to_string_proper(0x13), "1.3"); +	assert(errno == 0); + +	assert_streq(libar2_version_to_string_proper(0x11), NULL); +	assert(errno == EINVAL); +	errno = 0; + +	assert_streq(libar2_version_to_string_proper(0x12), NULL); +	assert(errno == EINVAL); +	errno = 0; + +	assert_streq(libar2_version_to_string_proper(0), NULL); +	assert(errno == EINVAL); +	errno = 0; +} + + +static void +check_libar2_string_to_version(void) +{ +	enum libar2_argon2_version version; + +	errno = 0; + +	assert(!libar2_string_to_version("10", &version) && version == LIBAR2_ARGON2_VERSION_10); +	assert(errno == 0); + +	assert(!libar2_string_to_version("13", &version) && version == LIBAR2_ARGON2_VERSION_13); +	assert(errno == 0); + +	assert(!libar2_string_to_version("1.0", &version) && version == LIBAR2_ARGON2_VERSION_10); +	assert(errno == 0); + +	assert(!libar2_string_to_version("1.3", &version) && version == LIBAR2_ARGON2_VERSION_13); +	assert(errno == 0); + +	assert(libar2_string_to_version("11", &version) == -1); +	assert(errno == EINVAL); +	errno = 0; + +	assert(libar2_string_to_version("12", &version) == -1); +	assert(errno == EINVAL); +	errno = 0; + +	assert(libar2_string_to_version("1.1", &version) == -1); +	assert(errno == EINVAL); +	errno = 0; + +	assert(libar2_string_to_version("1.2", &version) == -1); +	assert(errno == EINVAL); +	errno = 0; + +	assert(libar2_string_to_version("16", &version) == -1); +	assert(errno == EINVAL); +	errno = 0; + +	assert(libar2_string_to_version("19", &version) == -1); +	assert(errno == EINVAL); +	errno = 0; +} + + +static void +check_libar2_encode_base64(void) +{ +	char buf[128]; + +	errno = 0; + +	assert(libar2_encode_base64(NULL, MEM("")) == 1); +	assert(libar2_encode_base64(NULL, MEM("1")) == 3); +	assert(libar2_encode_base64(NULL, MEM("12")) == 4); +	assert(libar2_encode_base64(NULL, MEM("123")) == 5); +	assert(libar2_encode_base64(NULL, MEM("1234")) == 7); +	assert(libar2_encode_base64(NULL, MEM("12345")) == 8); +	assert(errno == 0); + +	assert(libar2_encode_base64(buf, MEM("")) == 1); +	assert_streq(buf, ""); +	assert(errno == 0); + +	assert(libar2_encode_base64(buf, MEM("\x00")) == 3); +	assert_streq(buf, "AA"); +	assert(errno == 0); + +	assert(libar2_encode_base64(buf, MEM("\x00\x00")) == 4); +	assert_streq(buf, "AAA"); +	assert(errno == 0); + +	assert(libar2_encode_base64(buf, MEM("\x00\x00\x00")) == 5); +	assert_streq(buf, "AAAA"); +	assert(errno == 0); + +	assert(libar2_encode_base64(buf, MEM("12345678")) == 12); +	assert_streq(buf, "MTIzNDU2Nzg"); +	assert(errno == 0); + +	assert(libar2_encode_base64(buf, MEM("testtest")) == 12); +	assert_streq(buf, "dGVzdHRlc3Q"); +	assert(errno == 0); + +	assert(libar2_encode_base64(buf, MEM("zy[]y21 !")) == 13); +	assert_streq(buf, "enlbXXkyMSAh"); +	assert(errno == 0); + +	assert(libar2_encode_base64(buf, MEM("{~|~}~~~\x7f\x7f")) == 15); +	assert_streq(buf, "e358fn1+fn5/fw"); +	assert(errno == 0); +} + + +static void +check_libar2_decode_base64(void) +{ +	char buf[128]; +	size_t len; + +	errno = 0; + +	assert(libar2_decode_base64("", buf, &len) == 0); +	assert(len == 0); +	assert(errno == 0); + +	assert(libar2_decode_base64("A", buf, &len) == 0); +	assert(len == 0); +	assert(errno == 0); + +#define CHECK(S) len == sizeof(S) - 1 && !memcmp(buf, S, len) + +	assert(libar2_decode_base64("AA", buf, &len) == 2); +	assert(CHECK("\x00")); +	assert(errno == 0); + +	assert(libar2_decode_base64("AAA", buf, &len) == 3); +	assert(CHECK("\x00\x00")); +	assert(errno == 0); + +	assert(libar2_decode_base64("AAAA", buf, &len) == 4); +	assert(CHECK("\x00\x00\x00")); +	assert(errno == 0); + +	assert(libar2_decode_base64("AAAAA", buf, &len) == 4); +	assert(CHECK("\x00\x00\x00")); +	assert(errno == 0); + +	assert(libar2_decode_base64("AAAAAA", buf, &len) == 6); +	assert(CHECK("\x00\x00\x00\x00")); +	assert(errno == 0); + +	assert(libar2_decode_base64("MTIzNDU2Nzg", buf, &len) == 11); +	assert(CHECK("12345678")); +	assert(errno == 0); + +	assert(libar2_decode_base64("dGVzdHRlc3Q", buf, &len) == 11); +	assert(CHECK("testtest")); +	assert(errno == 0); + +	assert(libar2_decode_base64("enlbXXkyMSAh", buf, &len) == 12); +	assert(CHECK("zy[]y21 !")); +	assert(errno == 0); + +	assert(libar2_decode_base64("e358fn1+fn5/fw", buf, &len) == 14); +	assert(CHECK("{~|~}~~~\x7f\x7f")); +	assert(errno == 0); + +#undef CHECK +} + + +static void +check_libar2_encode_params_libar2_decode_params(void) +{ +	struct libar2_argon2_parameters params; +	char *sbuf = NULL; +	char pbuf[256]; + +#define DECODE(PARAMS, HASH)\ +	libar2_decode_params(PARAMS""HASH, ¶ms, &sbuf, &ctx_st) + +#define PARAMSTR "$argon2i$v=19$m=4096,t=3,p=1$fn5/f35+f38$" +	memset(¶ms, 0xFF, sizeof(params)); +	assert_zueq(DECODE(PARAMSTR, "1234"), sizeof(PARAMSTR) - 1); +	assert(params.type == LIBAR2_ARGON2I); +	assert(params.version == LIBAR2_ARGON2_VERSION_13); +	assert(params.t_cost == 3); +	assert(params.m_cost == 4096); +	assert(params.lanes == 1); +	assert(params.salt != NULL); +	assert(params.saltlen == 8); +	assert(!memcmp(params.salt, "~~\x7f\x7f~~\x7f\x7f", params.saltlen)); +	assert(!params.key); +	assert(!params.keylen); +	assert(!params.ad); +	assert(!params.adlen); +	assert(params.hashlen == 3); +	assert_zueq(libar2_encode_params(NULL, ¶ms), sizeof(PARAMSTR)); +	assert_zueq(libar2_encode_params(pbuf, ¶ms), sizeof(PARAMSTR)); +	assert_streq(pbuf, PARAMSTR); +	assert(sbuf != NULL); +	ctx_st.deallocate(sbuf, &ctx_st); +	sbuf = NULL; +#undef PARAMSTR + +#undef DECODE +} + + +static void +check_libar2_validate_params(void) +{ +	struct libar2_argon2_parameters params; +	const char *errmsg = NULL; + +	errno = 0; + +	memset(¶ms, 0, sizeof(params)); +	params.type = LIBAR2_ARGON2I; +	params.version = LIBAR2_ARGON2_VERSION_13; +	params.t_cost = 3; +	params.m_cost = 4096; +	params.lanes = 1; +	params.saltlen = 8; +	params.hashlen = 4; +	assert(libar2_validate_params(¶ms, &errmsg) == LIBAR2_OK); +	assert_streq(errmsg, "OK"); +	assert(errno == 0); +	errmsg = NULL; +	assert(libar2_validate_params(¶ms, &errmsg) == 0); +	assert_streq(errmsg, "OK"); +	assert(errno == 0); +	errmsg = NULL; +	assert(libar2_validate_params(¶ms, NULL) == 0); +	assert(errno == 0); + +	params.version = LIBAR2_ARGON2_VERSION_10; +	assert(libar2_validate_params(¶ms, NULL) == 0); +	assert(errno == 0); +	params.type = LIBAR2_ARGON2I; +	assert(libar2_validate_params(¶ms, NULL) == 0); +	assert(errno == 0); +	params.type = LIBAR2_ARGON2D; +	assert(libar2_validate_params(¶ms, NULL) == 0); +	assert(errno == 0); +	params.type = LIBAR2_ARGON2DS; +	assert(libar2_validate_params(¶ms, NULL) == 0); +	assert(errno == 0); + +	params.hashlen = 3; +	assert(libar2_validate_params(¶ms, &errmsg) == LIBAR2_HASH_TOO_SMALL); +	assert_streq(errmsg, "tag length parameter is too small"); +	assert(errno == 0); +	errmsg = NULL; +	assert(libar2_validate_params(¶ms, NULL) == LIBAR2_HASH_TOO_SMALL); +	assert(errno == 0); +	params.hashlen = 4; + +	params.saltlen = 7; +	assert(libar2_validate_params(¶ms, &errmsg) == LIBAR2_SALT_TOO_SMALL); +	assert_streq(errmsg, "salt parameter is too small"); +	assert(errno == 0); +	errmsg = NULL; +	assert(libar2_validate_params(¶ms, NULL) == LIBAR2_SALT_TOO_SMALL); +	assert(errno == 0); +	params.saltlen = 8; + +	params.t_cost = 0; +	assert(libar2_validate_params(¶ms, &errmsg) == LIBAR2_T_COST_TOO_SMALL); +	assert_streq(errmsg, "time-cost parameter is too small"); +	assert(errno == 0); +	errmsg = NULL; +	assert(libar2_validate_params(¶ms, NULL) == LIBAR2_T_COST_TOO_SMALL); +	assert(errno == 0); +	params.t_cost = 1; + +	params.m_cost = 7; +	assert(libar2_validate_params(¶ms, &errmsg) == LIBAR2_M_COST_TOO_SMALL); +	assert_streq(errmsg, "memory-cost parameter is too small"); +	assert(errno == 0); +	errmsg = NULL; +	assert(libar2_validate_params(¶ms, NULL) == LIBAR2_M_COST_TOO_SMALL); +	assert(errno == 0); +	params.m_cost = 8; + +	params.lanes = 0; +	assert(libar2_validate_params(¶ms, &errmsg) == LIBAR2_TOO_FEW_LANES); +	assert_streq(errmsg, "lane-count parameter is too small"); +	assert(errno == 0); +	errmsg = NULL; +	assert(libar2_validate_params(¶ms, NULL) == LIBAR2_TOO_FEW_LANES); +	assert(errno == 0); +	params.lanes = 1; + +	params.lanes = 0x1000000UL; +	assert(libar2_validate_params(¶ms, &errmsg) == LIBAR2_TOO_MANY_LANES); +	assert_streq(errmsg, "lane-count parameter is too large"); +	assert(errno == 0); +	errmsg = NULL; +	assert(libar2_validate_params(¶ms, NULL) == LIBAR2_TOO_MANY_LANES); +	assert(errno == 0); +	params.lanes = 1; + +	params.type = -1; +	assert(libar2_validate_params(¶ms, &errmsg) == LIBAR2_INVALID_TYPE); +	assert_streq(errmsg, "type parameter is invalid"); +	assert(errno == 0); +	errmsg = NULL; +	assert(libar2_validate_params(¶ms, NULL) == LIBAR2_INVALID_TYPE); +	assert(errno == 0); +	params.type = 0; + +	params.type = 3; +	assert(libar2_validate_params(¶ms, &errmsg) == LIBAR2_INVALID_TYPE); +	assert_streq(errmsg, "type parameter is invalid"); +	assert(errno == 0); +	errmsg = NULL; +	assert(libar2_validate_params(¶ms, NULL) == LIBAR2_INVALID_TYPE); +	assert(errno == 0); +	params.type = 0; + +	params.type = 5; +	assert(libar2_validate_params(¶ms, &errmsg) == LIBAR2_INVALID_TYPE); +	assert_streq(errmsg, "type parameter is invalid"); +	assert(errno == 0); +	errmsg = NULL; +	assert(libar2_validate_params(¶ms, NULL) == LIBAR2_INVALID_TYPE); +	assert(errno == 0); +	params.type = 0; + +	params.version = 0x11; +	assert(libar2_validate_params(¶ms, &errmsg) == LIBAR2_INVALID_VERSION); +	assert_streq(errmsg, "version parameter is invalid"); +	assert(errno == 0); +	errmsg = NULL; +	assert(libar2_validate_params(¶ms, NULL) == LIBAR2_INVALID_VERSION); +	assert(errno == 0); +	params.type = 0x10; +} + + +static void +check_hash(const char *pwd_, size_t pwdlen, const char *hash, struct libar2_context *ctx, int lineno) +{ +	struct libar2_argon2_parameters params; +	char *sbuf, output[512], pwd[512], output64[700]; +	size_t plen; + +	from_lineno = lineno; +	errno = 0; + +	stpcpy(pwd, pwd_); +	plen = libar2_decode_params(hash, ¶ms, &sbuf, ctx); +	assert(!libar2_validate_params(¶ms, NULL)); +	assert(!libar2_hash(output, pwd, pwdlen, ¶ms, ctx)); +	libar2_encode_base64(output64, output, params.hashlen); +	assert_streq(output64, &hash[plen]); +	assert(errno == 0); +	if (sbuf) { +		ctx->deallocate(sbuf, ctx); +	} + +	from_lineno = 0; +} + + +static void +check_libar2_hash(void) +{ +#define CHECK(PWD, HASH)\ +	check_hash(MEM(PWD), HASH, &ctx_st, __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=1048576,t=2,p=1$c29tZXNhbHQ$lpDsVdKNPtMlYvLnPqYrArAYdXZDoq5ueVKEWd6BBuk"); +	CHECK("password", "$argon2i$m=262144,t=2,p=1$c29tZXNhbHQ$Pmiaqj0op3zyvHKlGsUxZnYXURgvHuKS4/Z3p9pMJGc"); +	CHECK("password", "$argon2i$m=256,t=2,p=1$c29tZXNhbHQ$/U3YPXYsSb3q9XxHvc0MLxur+GP960kN9j7emXX8zwY"); +	CHECK("password", "$argon2i$m=256,t=2,p=2$c29tZXNhbHQ$tsEVYKap1h6scGt5ovl9aLRGOqOth+AMB+KwHpDFZPs"); +	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=19$m=1048576,t=2,p=1$c29tZXNhbHQ$0Vh6ygkiw7XWqD7asxvuPE667zQu1hJ6VdGbI1GtH0E"); +	CHECK("password", "$argon2i$v=19$m=262144,t=2,p=1$c29tZXNhbHQ$KW266AuAfNzqrUSudBtQbxTbCVkmexg7EY+bJCKbx8s"); +	CHECK("password", "$argon2i$v=19$m=256,t=2,p=1$c29tZXNhbHQ$iekCn0Y3spW+sCcFanM2xBT63UP2sghkUoHLIUpWRS8"); +	CHECK("password", "$argon2i$v=19$m=256,t=2,p=2$c29tZXNhbHQ$T/XOJ2mh1/TIpJHfCdQan76Q5esCFVoT5MAeIM1Oq2E"); +	CHECK("password", "$argon2i$v=19$m=65536,t=1,p=1$c29tZXNhbHQ$0WgHXE2YXhPr6uVgz4uUw7XYoWxRkWtvSsLaOsEbvs8"); +	CHECK("password", "$argon2i$v=19$m=65536,t=4,p=1$c29tZXNhbHQ$qqlT1YrzcGzj3xrv1KZKhOMdf1QXUjHxKFJZ+IF0zls"); +	CHECK("differentpassword", "$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$FK6NoBr+qHAMI1jc73xTWNkCEoK9iGY6RWL1n7dNIu4"); +	CHECK("password", "$argon2i$v=19$m=65536,t=2,p=1$ZGlmZnNhbHQ$sDV8zPvvkfOGCw26RHsjSMvv7K2vmQq/6cxAcmxSEnE"); + +	CHECK("password", "$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc"); +	CHECK("password", "$argon2id$v=19$m=262144,t=2,p=1$c29tZXNhbHQ$eP4eyR+zqlZX1y5xCFTkw9m5GYx0L5YWwvCFvtlbLow"); +	CHECK("password", "$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"); +	CHECK("password", "$argon2id$v=19$m=256,t=2,p=2$c29tZXNhbHQ$bQk8UB/VmZZF4Oo79iDXuL5/0ttZwg2f/5U52iv1cDc"); +	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", "$argon2id$v=19$m=65536,t=2,p=1$ZGlmZnNhbHQ$vfMrBczELrFdWP0ZsfhWsRPaHppYdP3MVEMIVlqoFBw"); + +#undef CHECK +} + + +int +main(void) +{ +	check_libar2_type_to_string(); +	check_libar2_string_to_type(); +	check_libar2_version_to_string(); +	check_libar2_version_to_string_proper(); +	check_libar2_string_to_version(); +	check_libar2_encode_base64(); +	check_libar2_decode_base64(); +	check_libar2_encode_params_libar2_decode_params(); +	check_libar2_validate_params(); +	check_libar2_hash(); +} | 
