aboutsummaryrefslogblamecommitdiffstats
path: root/libar2.h
blob: 3b14b3ae7bfb34fe1a1cfb62a965ebaf86212f50 (plain) (tree)
1
2
3
4
5
6
7






                                                         




                                            

                        





                                                                    















                                                        




















                                                                                                                                 
                                                                                                            




































































                                                                                                          
                                                   













                                                        
                                                   






































                                           

                                                              

































































































































































                                                                                                 
                                                               
                                                            
                                                 

















































































































                                                                                                              
                                                 
   
                                                                     









                                                                      
               








                                                                                           
                                      









                                                                           
               









                                                                         
               








                                                                                       
                                      
















                                                                                    
                                   








                                                                                      

                                            



                                                         
               


































                                                                         
                                            









                                                                                                                               
                                      








                                                                       
                                   


                                                                                                                        
                        



                                             
               
                                                   


















                                                          
                                                       





                                                           

                                                                    






                                                                      
                                         

                                                                                                                           





                                                  
                                 


                                                       


                                                        
   
                                   
                                                                           
 



                             
      
/* See LICENSE file for copyright and license details. */
#ifndef LIBAR2_H
#define LIBAR2_H

#include <stddef.h>
#include <stdint.h>

#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__)))
#else
# define LIBAR2_NONNULL__(...)
#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
 * 
 * @param   params  Hashing parameters
 * @return          The required allocation size of the
 *                  output parameter of `libar2_hash`, 0
 *                  with errno set to EOVERFLOW if the
 *                  result is too large
 */
LIBAR2_PUBLIC__ LIBAR2_NONNULL__(1)
size_t libar2_hash_buf_size(const struct libar2_argon2_parameters *params);

#if defined(__clang__)
# pragma clang diagnostic pop
#endif

#endif