diff options
-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(); +} |