/* See LICENSE file for copyright and license details. */ #ifndef LIBAR2_H #define LIBAR2_H #include #include #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" #endif /* for internal use { */ #if defined(__GNUC__) # define LIBAR2_NONNULL__(...) __attribute__((nonnull(__VA_ARGS__))) # define LIBAR2_PURE__ __attribute__((pure)) #else # define LIBAR2_NONNULL__(...) # define LIBAR2_PURE__ #endif #ifndef LIBAR2_PUBLIC__ # if defined(_MSC_VER) # define LIBAR2_PUBLIC__ __declspec(dllexport) # else # define LIBAR2_PUBLIC__ # endif #endif #ifndef LIBAR2_PUBLIC_VARIABLE__ # if defined(_MSC_VER) # define LIBAR2_PUBLIC_VARIABLE__ __declspec(dllexport) # else # define LIBAR2_PUBLIC_VARIABLE__ # endif #endif #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) || (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 environments! */ 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; or 0 if not specified * (which is equivalent to `LIBAR2_ARGON2_VERSION_10`) * * `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 erase the memory before * deallocating it; this is not done by the library. * This can be done using `libar2_erase`; * * 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 version of Argon2 that is supported */ extern const 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 */ LIBAR2_PUBLIC__ 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 */ LIBAR2_PUBLIC__ LIBAR2_NONNULL__(1, 2) 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 */ LIBAR2_PUBLIC__ 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 */ LIBAR2_PUBLIC__ 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 */ LIBAR2_PUBLIC__ LIBAR2_NONNULL__(1, 2) 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 */ LIBAR2_PUBLIC__ LIBAR2_NONNULL__(2) 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, may be * `NULL` if `buf` is `NULL` * @param len The number of bytes in `data` * @return The number of bytes required for `buf`, * including the NUL byte added to the end */ LIBAR2_PUBLIC__ 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 */ LIBAR2_PUBLIC__ LIBAR2_NONNULL__(1, 2, 3, 4) 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 */ LIBAR2_PUBLIC__ LIBAR2_NONNULL__(1, 3) 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 */ LIBAR2_PUBLIC__ LIBAR2_NONNULL__(1) enum libar2_parameter_error libar2_validate_params(const struct libar2_argon2_parameters *params, const char **errmsgp); /** * Securily erase memory * * @param mem The memory to erase * @param size The number of bytes to erase */ LIBAR2_PUBLIC__ void libar2_erase(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 tried * 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 `libar2_hash_buf_size(params)` 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 */ LIBAR2_PUBLIC__ LIBAR2_NONNULL__(1, 4, 5) int libar2_hash(void *hash, void *msg, size_t msglen, struct libar2_argon2_parameters *params, struct libar2_context *ctx); /** * Return the number of bytes that is required for * the first parameter, the output parmeter, of * `libar2_hash` * * If `params->hashlen <= 64`, this function will * return `params->hashlen` as is, otherwise it * will return a value that is no greater than * `params->hashlen + 127` * * @param params Hashing parameters * @return The required allocation size of the * output parameter of `libar2_hash` */ LIBAR2_PUBLIC__ LIBAR2_NONNULL__(1) LIBAR2_PURE__ size_t libar2_hash_buf_size(struct libar2_argon2_parameters *params); #if defined(__clang__) # pragma clang diagnostic pop #endif #endif