From b29f4153e83623f24bebe99976e1368ef31bb008 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Thu, 21 May 2026 17:12:20 +0200 Subject: Add support for custom hash functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- Makefile | 13 +- README | 30 +- TODO | 3 +- argon2/hash.c | 4 +- argon2/test_supported.c | 2 +- common.h | 246 +----------- librecrypt.7 | 32 +- librecrypt.h | 306 +++++++++++++- librecrypt_check_settings_.c | 799 ------------------------------------- librecrypt_context_get_pepper_.c | 63 --- librecrypt_context_get_user_data.c | 29 -- librecrypt_context_set_pepper.c | 37 -- librecrypt_context_set_user_data.c | 29 -- librecrypt_create_context.3 | 76 ++++ librecrypt_create_context.c | 17 +- librecrypt_crypt.3 | 4 +- librecrypt_find_first_algorithm_.c | 16 +- librecrypt_free_context.3 | 55 +++ librecrypt_free_context.c | 12 +- librecrypt_get_pepper_.c | 152 +++++++ librecrypt_get_user_data.3 | 49 +++ librecrypt_get_user_data.c | 24 ++ librecrypt_hash.3 | 4 +- librecrypt_hash_.c | 9 +- librecrypt_hash_algorithm_end.3 | 44 ++ librecrypt_hash_algorithm_end.c | 2 +- librecrypt_hash_binary.3 | 4 +- librecrypt_is_enabled.3 | 61 +-- librecrypt_make_settings.3 | 6 +- librecrypt_realise_salts.3 | 4 +- librecrypt_realise_salts.c | 2 +- librecrypt_scan_settings.3 | 295 ++++++++++++++ librecrypt_scan_settings.c | 799 +++++++++++++++++++++++++++++++++++++ librecrypt_set_custom_algorithms.3 | 70 ++++ librecrypt_set_custom_algorithms.c | 51 +++ librecrypt_set_pepper.3 | 137 +++++++ librecrypt_set_pepper.c | 143 +++++++ librecrypt_set_user_data.3 | 49 +++ librecrypt_set_user_data.c | 49 +++ librecrypt_settings_prefix.3 | 2 +- librecrypt_settings_prefix.c | 8 +- 41 files changed, 2432 insertions(+), 1305 deletions(-) delete mode 100644 librecrypt_check_settings_.c delete mode 100644 librecrypt_context_get_pepper_.c delete mode 100644 librecrypt_context_get_user_data.c delete mode 100644 librecrypt_context_set_pepper.c delete mode 100644 librecrypt_context_set_user_data.c create mode 100644 librecrypt_create_context.3 create mode 100644 librecrypt_free_context.3 create mode 100644 librecrypt_get_pepper_.c create mode 100644 librecrypt_get_user_data.3 create mode 100644 librecrypt_get_user_data.c create mode 100644 librecrypt_hash_algorithm_end.3 create mode 100644 librecrypt_scan_settings.3 create mode 100644 librecrypt_scan_settings.c create mode 100644 librecrypt_set_custom_algorithms.3 create mode 100644 librecrypt_set_custom_algorithms.c create mode 100644 librecrypt_set_pepper.3 create mode 100644 librecrypt_set_pepper.c create mode 100644 librecrypt_set_user_data.3 create mode 100644 librecrypt_set_user_data.c diff --git a/Makefile b/Makefile index 57453a5..6485a9c 100644 --- a/Makefile +++ b/Makefile @@ -42,9 +42,10 @@ OBJ_PUBLIC_NO_FUZZ =\ librecrypt_hash_algorithm_end.o\ librecrypt_create_context.o\ librecrypt_free_context.o\ - librecrypt_context_set_user_data.o\ - librecrypt_context_get_user_data.o\ - librecrypt_context_set_pepper.o + librecrypt_set_user_data.o\ + librecrypt_get_user_data.o\ + librecrypt_set_pepper.o\ + librecrypt_set_custom_algorithms.o OBJ_PUBLIC =\ $(OBJ_PUBLIC_FUZZ)\ @@ -55,7 +56,8 @@ OBJ_PUBLIC =\ librecrypt_hash.o\ librecrypt_crypt.o\ librecrypt_add_algorithm.o\ - librecrypt_verify.o + librecrypt_verify.o\ + librecrypt_scan_settings.o OBJ_PRIVATE =\ librecrypt_algorithms_.o\ @@ -63,8 +65,7 @@ OBJ_PRIVATE =\ librecrypt_hash_.o\ librecrypt_fill_with_random_.o\ librecrypt_find_first_algorithm_.o\ - librecrypt_check_settings_.o\ - librecrypt_context_get_pepper_.o\ + librecrypt_get_pepper_.o\ $(OBJ_COMMON_RFC4848S4) USE_OBJ_COMMON_RFC4848S4 =\ diff --git a/README b/README index f1f115b..197e6eb 100644 --- a/README +++ b/README @@ -58,7 +58,7 @@ DESCRIPTION Realise asterisk-encoded random salts in a settings string. - Secure application helper functions: + Application helper functions: librecrypt_equal(3) Compare strings in constant time. @@ -71,6 +71,9 @@ DESCRIPTION librecrypt_wipe_str(3) Securely erase a string. + librecrypt_scan_settings(3) (ADVANCED) + Parse and validate a password hash string. + Password hash string decomposition functions (ADVANCED): librecrypt_settings_prefix(3) Get length of settings prefix in a password hash @@ -103,11 +106,34 @@ DESCRIPTION Get encoding alphabet for the last algorithm in a chain. - Library introspection functions (ADVANCED): + Library introspection (ADVANCED): librecrypt_is_enabled(3) Check whenther the library is compiled with support for a specific hash algorithm. + librecrypt_hash_algorithm_end(3) + Number of hash algorithms the linked library knows + about. + + Library tweaking functions (ADVANCED): + librecrypt_create_context(3) + Create a library configuration object. + + librecrypt_free_context(3) + Deallocate a library configuration object. + + librecrypt_set_pepper(3) + Set the pepper to use for a hash algorithm. + + librecrypt_set_custom_algorithms(3) + Set application-provided hash algorithms. + + librecrypt_set_user_data(3) + Set application-defined data. + + librecrypt_get_user_data(3) + Retrieve application-defined data. + NOTES Using librecrypt_add_algorithm(3) to hash existing password hashes should be used as a transitional mitigation strategy diff --git a/TODO b/TODO index 2919334..3144966 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,4 @@ +Document `struct librecrypt_algorithm` in librecrypt_set_custom_algorithms.3 Write fuzzing code for the files listed in OBJ_PUBLIC. Add corpuses for fuzzing. Add documentation similar to crypt(5). @@ -16,5 +17,3 @@ Add support for bsdicrypt. Add support for descrypt. Add support for bigcrypt. Add support for NT Hash. -Add support for custom hash functions. -Add support (via the "ctx" parameter) for pepper. diff --git a/argon2/hash.c b/argon2/hash.c index e498d35..949841b 100644 --- a/argon2/hash.c +++ b/argon2/hash.c @@ -108,7 +108,7 @@ librecrypt__argon2__hash(char *restrict out_buffer, size_t size, const char *phr (void) ctx; /* Parse `settings` */ - r = librecrypt_scan_settings_(settings, prefix, + r = librecrypt_scan_settings(settings, prefix, "$argon2%^s$%^sm=%^p,t=%^p,p=%^p$%&b$%^h", &type, "id", "i", "ds", "d", NULL, /* order partially matters */ &version, "v=19$", "v=16$", "", NULL, /* empty string last */ @@ -215,7 +215,7 @@ librecrypt__argon2__hash(char *restrict out_buffer, size_t size, const char *phr default: abort(); /* $covered$ (impossible) */ } - pepper = librecrypt_context_get_pepper_(ctx, algo, 0u); + pepper = librecrypt_get_pepper_(ctx, algo, 0u); no_pepper: params.t_cost = (uint_least32_t)tcost; params.m_cost = (uint_least32_t)mcost; diff --git a/argon2/test_supported.c b/argon2/test_supported.c index f41188a..cad7794 100644 --- a/argon2/test_supported.c +++ b/argon2/test_supported.c @@ -20,7 +20,7 @@ librecrypt__argon2__test_supported(const char *phrase, size_t len, int text, con (void) text; /* Validate string format and parameters */ - r = librecrypt_scan_settings_(settings, prefix, + r = librecrypt_scan_settings(settings, prefix, "$%*$%sm=%p,t=%p,p=%p$%b$%^h", "v=16$", "v=19$", "", NULL, RANGE(LIBAR2_MIN_M_COST, LIBAR2_MAX_M_COST), diff --git a/common.h b/common.h index b329fca..e020e11 100644 --- a/common.h +++ b/common.h @@ -9,6 +9,7 @@ # pragma clang diagnostic ignored "-Wimplicit-void-ptr-cast" /* C++ warning, and we are in internal files */ # pragma clang diagnostic ignored "-Wc++-keyword" /* C++ warning, and we are in internal files */ # pragma clang diagnostic ignored "-Wc++-unterminated-string-initialization" /* Stupid C++ warning, and we are in internal files */ +# pragma clang diagnostic ignored "-Wcovered-switch-default" /* harmful warning */ #endif #if defined(__GNUC__) # pragma GCC diagnostic ignored "-Winline" @@ -134,185 +135,24 @@ struct librecrypt_context { void *user_data; /** - * Per hash algorithm peppers - */ - struct pepper peppers[LIBRECRYPT_HASH_ALGORITHM_END]; - /* TODO we probably don't want slots allocated for - * algorithms that have been disabled */ -}; - - -/** - * Hash algorithm information and implementation - * - * Current limitations: - * - * - The algorithm must not use the '*' symbol except - * in the way the librecrypt library uses '*' for - * specifying sizes, in bytes (after base64-decoding), - * of randomised data (salt) and the hash result - * - * - The algorithm must not use the '>' symbol - * - * - The hash must be att the end, immediately after - * the last '$' (some expections exists for legacy - * hash algorithms), and empty must be usable to - * specify default hash size - * - * - Salts and hashes must be encoded in some variant - * of base64 where the bytes (represented with the - * most significant bit first) a₇a₆a₅a₄a₃a₂a₁a₀, - * b₇b₆b₅b₄b₃b₂b₁b₀, and c₇c₆c₅c₄c₃c₂c₁c₀ are - * rearranged to a₇a₆a₅a₄a₃a₂, a₁a₀b₇b₆b₅b₄, - * b₃b₂b₁b₀c₇c₆, c₅c₄c₃c₂c₁c₀, and missing bits - * are set to 0; and padding if supported at all, - * is only allowed, up to 3 pad letters, at the - * end to pad the output to a multiple of 4 letters - */ -struct librecrypt_algorithm { - /** - * Determine if a password hash string - * selects the algorithm - * - * @param settings The password hash string, containing a single algorithm - * @param len The number of bytes in `settings` - * @return A positive value if the string matches the algorithm, - * 0 otherwise - * - * If non-zero is returned for multiple algorithm, - * the first with the highest value wins - * - * This function shall be MT-Safe and AS-Safe - */ - unsigned (*is_algorithm)(const char *settings, size_t len); - - /** - * Implements `librecrypt_hash_binary` for a single hash algorithm; - * see `librecrypt_hash_binary` for more information - * - * @param out_buffer See `librecrypt_hash_binary` - * @param size See `librecrypt_hash_binary` - * @param phrase See `librecrypt_hash_binary`; may be `NULL` - * even if `len` is positive, this happens when - * `size` is too small and the hash result will - * not be included, so there is no need to actually - * calculate the hash, however `len` and `settings` - * should still be checked - * @param len See `librecrypt_hash_binary` - * @param settings See `librecrypt_hash_binary`, - * will not contains asterisk-encoding - * @param prefix The length of `settings`, in bytes - * @param ctx See `librecrypt_hash_binary` - * @return 0 on success, -1 on failure - * @throws See `librecrypt_hash_binary` - * - * This function shall be MT-Safe but may be AS-Unsafe + * Application-provided hash function implementations */ - int (*hash)(char *restrict out_buffer, size_t size, const char *phrase, size_t len, - const char *settings, size_t prefix, LIBRECRYPT_CONTEXT *ctx); + const struct librecrypt_algorithm *algos; /** - * Check whether the hash algorithm is supported for given - * configuration and input - * - * @param phrase The password to hash, may contain NUL bytes; - * may be `NULL` even if `len` is non-zero - * @param len The number of bytes in `phrase`, if `phrase` is `NULL`, - * the function will check that the specified number of - * bytes is supported as well as any byte sequence unless - * `text` is non-zero - * @param text Assume the password is valid UTF-8 text (without NUL bytes) - * iff non-zero; ignored if `phrase` is non-`NULL` - * @param settings The password hash string; it is allowed for algorithm - * tuning parameters, and the hash result, to be omitted - * @param prefix The number of bytes in `settings` - * @param len_out Output parameter for the binary hash size, in bytes - * @return 1 if the configuration is supported and correctly - * configured, 0 otherwise - * - * This function shall be MT-Safe and AS-Safe - */ - int (*test_supported)(const char *phrase, size_t len, int text, const char *settings, - size_t prefix, size_t *len_out); - - /** - * See `librecrypt_make_settings` - * - * @param out_buffer See `librecrypt_make_settings` - * @param size See `librecrypt_make_settings` - * @param algorithm See `librecrypt_make_settings`, - * will match the algorithm or be `NULL` - * @param memcost See `librecrypt_make_settings` - * @param timecost See `librecrypt_make_settings` - * @param gensalt See `librecrypt_make_settings` - * @param rng See `librecrypt_make_settings`, - * except the function will not be called - * with `rng` set to `NULL` - * @param user See `librecrypt_make_settings` - * @return See `librecrypt_make_settings` - * @throws See `librecrypt_make_settings` - * - * This function shall be MT-Safe but may be AS-Safe + * The number of elements in `.algos` */ - ssize_t (*make_settings)(char *out_buffer, size_t size, const char *algorithm, - size_t memcost, uintmax_t timecost, int gensalt, - ssize_t (*rng)(void *out, size_t n, void *user), void *user); + size_t nalgos; /** - * Expected argument for the `lut` parameter - * of the `librecrypt_encode` function - * - * This shall repeat a 64 character ASCII - * alphabet 4 times - */ - const char *encoding_lut; - - /** - * Expected argument for the `lut` parameter - * of the `librecrypt_decode` function - * - * This shall unique map the letters in - * `.encoding_lut` to there initial position - * in `.encoding_lut` (that's, uniquely to - * the range [0, 63]). All other bytes - * (including `.pad`) shall map to `0xFFu` - */ - const unsigned char *decoding_lut; - - /** - * The algoritm's hash result size, in number - * of bytes when using binary encoding - */ - size_t hash_size; - - /** - * 1 if `.hash_size` is just a default, - * 0 if `.hash_size` is always used - */ - signed char flexible_hash_size; - - /** - * Expected argument for the `strict_pad` parameter - * of the `librecrypt_decode` function - * - * Shall be either 1 (always pad when encoding, - * and require padding when decoding) or 0 - * (do not pad when encoding, but allow padding - * (provided that `.pad != 0`) when decoding) - */ - signed char strict_pad; - - /** - * Expected argument for the `pad` parameter - * of the `librecrypt_decode` function - * - * The pad character, used to pad base64-encoding - * to a multiple of 4 letters, shall be `'\0'` if - * not specified + * Per hash algorithm peppers */ - char pad; + struct pepper peppers[LIBRECRYPT_HASH_ALGORITHM_END]; + /* TODO we probably don't want slots allocated for + * algorithms that have been disabled */ }; + /** * Check if an `struct algorithm *` is the `END_OF_ALGORITHMS` * at the end of `librecrypt_algorithms_` @@ -499,71 +339,7 @@ const struct librecrypt_algorithm *librecrypt_find_first_algorithm_(const char * * for the hash algorithm `algo` */ LIBRECRYPT_NONNULL_1__ -struct pepper *librecrypt_context_get_pepper_(LIBRECRYPT_CONTEXT *ctx, enum librecrypt_hash_algorithm algo, size_t len); - - -/** - * Function used to validate a password hash string - * - * @param settings The string to validate - * @param len The number of bytes in `settings` - * @param fmt The expected format of the string, it may contain - * the metacharacter '%' (`fmt` is parsed left to right) - * to perform special string content checks: - * "%%" - Literal '%' - * "%*" - Any sequence of non-'$' bytes (greedly matched) - * "%s" - String - * "%u" - Unsigned integer that may start with a leading zeroes - * "%p" - Unsigned integer that most not start with a leading zeroes - * "%b" - Binary data, either encoded to ASCII or ungenerated content - * that is length specified using asterisk-notation - * "%h" - Same as "%b", except empty content as always allowed unless - * asterisk-notation is used - * "%^s" - Same as "%s" except with output argument - * "%^u" - Same as "%u" except with output argument - * "%^p" - Same as "%p" except with output argument - * "%^b" - Same as "%b" except with one output argument: length - * "%&b" - Same as "%b" except with two output argument: - * pointer to text, text length, or NULL and binary length - * "%^h" - Same as "%h" except with one output argument: length - * "%&h" - Same as "%h" except with two output argument: - * pointer to text, text length, or NULL and binary length - * @param ... Arguments for each use of '%' in `fmt`: - * "%%" - None - * "%*" - None - * "%s" - At least one `const char *`: allowed matches (in order of preference), - * followed by a `NULL` - * "%u" - Two `uintmax_t`: the minimum value and the maximum value - * "%p" - Same as "%u" - * "%b" - Two `uintmax_t`: the minimum value and the maximum value, - * one `const unsigned char dlut[static 256]`: the ASCII-encoding decoding table, - * one `char`: the padding character or `'\0'` if none may be used, and - * one `int`: whether padding must always be used unless the previous argument is `'\0'` - * "%h" - Same as "%b" - * "%^s" - Same as "%s" but with an additional argument, as the first one: - * a `const char **` used to store the matched string - * "%^u" - Same as "%u" but with an additional argument, as the first one: - * a `uintmax_t *` used to store the encoded integer - * "%^p" - Same as "%p" but with an additional argument, as the first one: - * a `uintmax_t *` used to store the encoded integer - * "%^b" - Same as "%b" but with an additional argument, as the first one: - * a `uintmax_t *` used to store the number of encoded bytes or - * the encoded integer after the asterisk if asterisk-encoding is used - * "%&b" - Same as "%b" but with two additional arguments, as the first two: - * a `const char **` and a `uintmax_t *`: if asterisk-notation is used - * the `const char *` will be set to `NULL` and the `uintmax_t` will be - * set to the encoded number, othererwise the `const char *` will be - * set to point to the position in `settings` where the base-64 encoded - * text begins and the `uintmax_t` will be set to length of the text - * (as encoded in base-64, _not_ as decoded to binary) - * "%^h" - Same as "%^b" - * "%&h" - Same as "%&b" - * @return 1 if `string` matches `fmt`, 0 otherwise - * - * This function will call abort(3) if misused. - */ -LIBRECRYPT_READ_MEM__(1, 2) LIBRECRYPT_NONNULL_I__(3) LIBRECRYPT_WUR__ HIDDEN -int librecrypt_scan_settings_(const char *settings, size_t len, const char *fmt, ...); +struct pepper *librecrypt_get_pepper_(LIBRECRYPT_CONTEXT *ctx, enum librecrypt_hash_algorithm algo, size_t len); diff --git a/librecrypt.7 b/librecrypt.7 index 8abf6d7..caf7eda 100644 --- a/librecrypt.7 +++ b/librecrypt.7 @@ -78,7 +78,7 @@ Generate a password hash settings string. Realise asterisk-encoded random salts in a settings string. .sp -.SS Secure application helper functions: +.SS Application helper functions: .TP .BR librecrypt_equal (3) Compare strings in constant time. @@ -91,6 +91,9 @@ Securely erase a memory buffer. .TP .BR librecrypt_wipe_str (3) Securely erase a string. +.TP +.BR librecrypt_scan_settings "(3) (ADVANCED)" +Parse and validate a password hash string. .sp .SS Password hash string decomposition functions (ADVANCED): .TP @@ -126,10 +129,35 @@ binary. Get encoding alphabet for the last algorithm in a chain. .sp -.SS Library introspection functions (ADVANCED): +.SS Library introspection (ADVANCED): +.TP .BR librecrypt_is_enabled (3) Check whenther the library is compiled with support for a specific hash algorithm. +.TP +.BR librecrypt_hash_algorithm_end (3) +Number of hash algorithms the linked library knows +about. +.sp +.SS Library tweaking functions (ADVANCED): +.TP +.BR librecrypt_create_context (3) +Create a library configuration object. +.TP +.BR librecrypt_free_context (3) +Deallocate a library configuration object. +.TP +.BR librecrypt_set_pepper (3) +Set the pepper to use for a hash algorithm. +.TP +.BR librecrypt_set_custom_algorithms (3) +Set application-provided hash algorithms. +.TP +.BR librecrypt_set_user_data (3) +Set application-defined data. +.TP +.BR librecrypt_get_user_data (3) +Retrieve application-defined data. .SH NOTES Using diff --git a/librecrypt.h b/librecrypt.h index a9e3a37..ebd2f10 100644 --- a/librecrypt.h +++ b/librecrypt.h @@ -14,25 +14,25 @@ # define LIBRECRYPT_NONNULL__ __attribute__((__nonnull__)) # define LIBRECRYPT_NONNULL_I__(I) __attribute__((__nonnull__(I))) # define LIBRECRYPT_WUR__ __attribute__((__warn_unused_result__)) -# define LIBRECRYPT_MALLOC__(D, D_ARG) __attribute__((__malloc__(D, D_ARG))) #else # define LIBRECRYPT_PURE__ # define LIBRECRYPT_CONST__ # define LIBRECRYPT_NONNULL__ # define LIBRECRYPT_NONNULL_I__(I) # define LIBRECRYPT_WUR__ -# define LIBRECRYPT_MALLOC__(D, D_ARG) #endif #if defined(__GNUC__) && !defined(__clang__) # define LIBRECRYPT_READ_STR__(S) __attribute__((__access__(read_only, S))) # define LIBRECRYPT_READ_MEM__(B, N) __attribute__((__access__(read_only, B, N))) # define LIBRECRYPT_WRITE_MEM__(B, N) __attribute__((__access__(write_only, B, N))) # define LIBRECRYPT_READ_WRITE_STR__(S) __attribute__((__access__(read_write, S))) +# define LIBRECRYPT_MALLOC__(D, D_ARG) __attribute__((__malloc__(D, D_ARG))) #else # define LIBRECRYPT_READ_STR__(S) # define LIBRECRYPT_READ_MEM__(B, N) # define LIBRECRYPT_WRITE_MEM__(B, N) # define LIBRECRYPT_READ_WRITE_STR__(S) +# define LIBRECRYPT_MALLOC__(D, D_ARG) #endif #define LIBRECRYPT_NONNULL_1__ LIBRECRYPT_NONNULL_I__(1) @@ -146,6 +146,182 @@ enum librecrypt_hash_algorithm { }; +/** + * Hash algorithm information and implementation + * + * This is provided so you can implement custom + * hash algorithms; must user's will not need this + * + * Current limitations: + * + * - The algorithm must not use the '*' symbol except + * in the way the librecrypt library uses '*' for + * specifying sizes, in bytes (after base64-decoding), + * of randomised data (salt) and the hash result + * + * - The algorithm must not use the '>' symbol + * + * - The hash must be att the end, immediately after + * the last '$' (some expections exists for legacy + * hash algorithms), and empty must be usable to + * specify default hash size + * + * - Salts and hashes must be encoded in some variant + * of base64 where the bytes (represented with the + * most significant bit first) a₇a₆a₅a₄a₃a₂a₁a₀, + * b₇b₆b₅b₄b₃b₂b₁b₀, and c₇c₆c₅c₄c₃c₂c₁c₀ are + * rearranged to a₇a₆a₅a₄a₃a₂, a₁a₀b₇b₆b₅b₄, + * b₃b₂b₁b₀c₇c₆, c₅c₄c₃c₂c₁c₀, and missing bits + * are set to 0; and padding if supported at all, + * is only allowed, up to 3 pad letters, at the + * end to pad the output to a multiple of 4 letters + * + * @since 1.1 + */ +struct librecrypt_algorithm { + /** + * Determine if a password hash string + * selects the algorithm + * + * @param settings The password hash string, containing a single algorithm + * @param len The number of bytes in `settings` + * @return A positive value if the string matches the algorithm, + * 0 otherwise + * + * If non-zero is returned for multiple algorithm, + * the first with the highest value wins + * + * This function shall be MT-Safe and AS-Safe + */ + unsigned (*is_algorithm)(const char *settings, size_t len); + + /** + * Implements `librecrypt_hash_binary` for a single hash algorithm; + * see `librecrypt_hash_binary` for more information + * + * @param out_buffer See `librecrypt_hash_binary` + * @param size See `librecrypt_hash_binary` + * @param phrase See `librecrypt_hash_binary`; may be `NULL` + * even if `len` is positive, this happens when + * `size` is too small and the hash result will + * not be included, so there is no need to actually + * calculate the hash, however `len` and `settings` + * should still be checked + * @param len See `librecrypt_hash_binary` + * @param settings See `librecrypt_hash_binary`, + * will not contains asterisk-encoding + * @param prefix The length of `settings`, in bytes + * @param ctx See `librecrypt_hash_binary` + * @return 0 on success, -1 on failure + * @throws See `librecrypt_hash_binary` + * + * This function shall be MT-Safe but may be AS-Unsafe + */ + int (*hash)(char *restrict out_buffer, size_t size, const char *phrase, size_t len, + const char *settings, size_t prefix, LIBRECRYPT_CONTEXT *ctx); + + /** + * Check whether the hash algorithm is supported for given + * configuration and input + * + * @param phrase The password to hash, may contain NUL bytes; + * may be `NULL` even if `len` is non-zero + * @param len The number of bytes in `phrase`, if `phrase` is `NULL`, + * the function will check that the specified number of + * bytes is supported as well as any byte sequence unless + * `text` is non-zero + * @param text Assume the password is valid UTF-8 text (without NUL bytes) + * iff non-zero; ignored if `phrase` is non-`NULL` + * @param settings The password hash string; it is allowed for algorithm + * tuning parameters, and the hash result, to be omitted + * @param prefix The number of bytes in `settings` + * @param len_out Output parameter for the binary hash size, in bytes + * @return 1 if the configuration is supported and correctly + * configured, 0 otherwise + * + * This function shall be MT-Safe and AS-Safe + */ + int (*test_supported)(const char *phrase, size_t len, int text, const char *settings, + size_t prefix, size_t *len_out); + + /** + * See `librecrypt_make_settings` + * + * @param out_buffer See `librecrypt_make_settings` + * @param size See `librecrypt_make_settings` + * @param algorithm See `librecrypt_make_settings`, + * will match the algorithm or be `NULL` + * @param memcost See `librecrypt_make_settings` + * @param timecost See `librecrypt_make_settings` + * @param gensalt See `librecrypt_make_settings` + * @param rng See `librecrypt_make_settings`, + * except the function will not be called + * with `rng` set to `NULL` + * @param user See `librecrypt_make_settings` + * @return See `librecrypt_make_settings` + * @throws See `librecrypt_make_settings` + * + * This function shall be MT-Safe but may be AS-Safe + */ + ssize_t (*make_settings)(char *out_buffer, size_t size, const char *algorithm, + size_t memcost, uintmax_t timecost, int gensalt, + ssize_t (*rng)(void *out, size_t n, void *user), void *user); + + /** + * Expected argument for the `lut` parameter + * of the `librecrypt_encode` function + * + * This shall repeat a 64 character ASCII + * alphabet 4 times + */ + const char *encoding_lut; + + /** + * Expected argument for the `lut` parameter + * of the `librecrypt_decode` function + * + * This shall unique map the letters in + * `.encoding_lut` to there initial position + * in `.encoding_lut` (that's, uniquely to + * the range [0, 63]). All other bytes + * (including `.pad`) shall map to `0xFFu` + */ + const unsigned char *decoding_lut; + + /** + * The algoritm's hash result size, in number + * of bytes when using binary encoding + */ + size_t hash_size; + + /** + * 1 if `.hash_size` is just a default, + * 0 if `.hash_size` is always used + */ + signed char flexible_hash_size; + + /** + * Expected argument for the `strict_pad` parameter + * of the `librecrypt_decode` function + * + * Shall be either 1 (always pad when encoding, + * and require padding when decoding) or 0 + * (do not pad when encoding, but allow padding + * (provided that `.pad != 0`) when decoding) + */ + signed char strict_pad; + + /** + * Expected argument for the `pad` parameter + * of the `librecrypt_decode` function + * + * The pad character, used to pad base64-encoding + * to a multiple of 4 letters, shall be `'\0'` if + * not specified + */ + char pad; +}; + /** * The value `LIBRECRYPT_HASH_ALGORITHM_END` as in the @@ -154,8 +330,7 @@ enum librecrypt_hash_algorithm { * * @since 1.1 */ -extern enum librecrypt_hash_algorithm librecrypt_hash_algorithm_end; /* TODO man */ - +extern const enum librecrypt_hash_algorithm librecrypt_hash_algorithm_end; /** @@ -1052,9 +1227,11 @@ ssize_t librecrypt_add_algorithm(char *out_buffer, size_t size, const char *auge * * @param ctx The object to deallocate * + * This function is MT-Safe but AS-Unsafe + * * @since 1.1 */ -void librecrypt_free_context(LIBRECRYPT_CONTEXT *ctx); /* TODO man */ +void librecrypt_free_context(LIBRECRYPT_CONTEXT *ctx); /** @@ -1066,13 +1243,15 @@ void librecrypt_free_context(LIBRECRYPT_CONTEXT *ctx); /* TODO man */ * * @throws ENOMEM Failed to allocate enough memory * - * @seealso librecrypt_context_set_user_data - * @seealso librecrypt_context_set_pepper + * @seealso librecrypt_set_pepper + * @seealso librecrypt_set_custom_algorithms + * + * This function is MT-Safe but AS-Unsafe * * @since 1.1 */ LIBRECRYPT_MALLOC__(librecrypt_free_context, 1) LIBRECRYPT_WUR__ -LIBRECRYPT_CONTEXT *librecrypt_create_context(void); /* TODO man */ +LIBRECRYPT_CONTEXT *librecrypt_create_context(void); /** @@ -1088,29 +1267,31 @@ LIBRECRYPT_CONTEXT *librecrypt_create_context(void); /* TODO man */ * only holds a reference to `user`, not a copy of it * * @seealso librecrypt_create_context - * @seealso librecrypt_context_get_user_data + * @seealso librecrypt_get_user_data + * @seealso librecrypt_set_custom_algorithms * * @since 1.1 */ LIBRECRYPT_NONNULL_1__ -void librecrypt_context_set_user_data(LIBRECRYPT_CONTEXT *ctx, void *user); /* TODO man */ +void librecrypt_set_user_data(LIBRECRYPT_CONTEXT *ctx, void *user); /** * Get application-defined data, in a library configuration object, - * which was set using the `librecrypt_context_set_user_data` function + * which was set using the `librecrypt_set_user_data` function * * @param ctx The library configuration object * @return user The application-defined data, `NULL` if * it hasn't been set (or if it was set to `NULL`) * * @seealso librecrypt_create_context - * @seealso librecrypt_context_set_user_data + * @seealso librecrypt_set_user_data + * @seealso librecrypt_set_custom_algorithms * * @since 1.1 */ -LIBRECRYPT_NONNULL_1__ LIBRECRYPT_PURE__ -void *librecrypt_context_get_user_data(LIBRECRYPT_CONTEXT *ctx); /* TODO man */ +LIBRECRYPT_NONNULL_1__ LIBRECRYPT_PURE__ LIBRECRYPT_WUR__ +void *librecrypt_get_user_data(const LIBRECRYPT_CONTEXT *ctx); /** @@ -1140,8 +1321,103 @@ void *librecrypt_context_get_user_data(LIBRECRYPT_CONTEXT *ctx); /* TODO man */ * * @since 1.1 */ +LIBRECRYPT_NONNULL_1__ LIBRECRYPT_WUR__ +int librecrypt_set_pepper(LIBRECRYPT_CONTEXT *ctx, enum librecrypt_hash_algorithm algo, const void *data, size_t len); + + +/** + * Set the custom hash algorithms + * + * @param ctx The library configuration object + * @param algos The hash algorithms implementions to support + * in addition to those implemented by the library + * @parma nalgos The number of elements in `algos` + * + * The caller is responsible for the lifetime of `algos`: + * deallocating it will deallocate it for `ctx` as it + * only holds a reference to `algos`, not a copy of it + * + * @seealso librecrypt_create_context + * @seealso librecrypt_set_user_data + * @seealso librecrypt_get_user_data + * @seealso librecrypt_scan_settings + * + * @since 1.1 + */ LIBRECRYPT_NONNULL_1__ -int librecrypt_context_set_pepper(LIBRECRYPT_CONTEXT *ctx, enum librecrypt_hash_algorithm algo, const void *data, size_t len); /* TODO man */ +void librecrypt_set_custom_algorithms(LIBRECRYPT_CONTEXT *ctx, const struct librecrypt_algorithm *algos, size_t nalgos); + + +/** + * Parse and validate a password hash string + * + * This function is provided as a helper function + * for those wishing to implement custom hash + * algorithms + * + * @param settings The string to validate + * @param len The number of bytes in `settings` + * @param fmt The expected format of the string, it may contain + * the metacharacter '%' (`fmt` is parsed left to right) + * to perform special string content checks: + * "%%" - Literal '%' + * "%*" - Any sequence of non-'$' bytes (greedily matched) + * "%s" - String + * "%u" - Unsigned integer that may start with a leading zeroes + * "%p" - Unsigned integer that must not start with a leading zeroes + * "%b" - Binary data, either encoded to ASCII or ungenerated content + * that is length specified using asterisk-notation + * "%h" - Same as "%b", except empty content as always allowed unless + * asterisk-notation is used + * "%^s" - Same as "%s" except with output argument + * "%^u" - Same as "%u" except with output argument + * "%^p" - Same as "%p" except with output argument + * "%^b" - Same as "%b" except with one output argument: length + * "%&b" - Same as "%b" except with two output argument: + * pointer to text, text length, or NULL and binary length + * "%^h" - Same as "%h" except with one output argument: length + * "%&h" - Same as "%h" except with two output argument: + * pointer to text, text length, or NULL and binary length + * @param ... Arguments for each use of '%' in `fmt`: + * "%%" - None + * "%*" - None + * "%s" - At least one `const char *`: allowed matches (in order of preference), + * followed by a `NULL` + * "%u" - Two `uintmax_t`: the minimum value and the maximum value + * "%p" - Same as "%u" + * "%b" - Two `uintmax_t`: the minimum value and the maximum value, + * one `const unsigned char[static 256]`: the ASCII-encoding decoding table, + * one `char`: the padding character or `'\0'` if none may be used, and + * one `int`: whether padding must always be used unless the previous argument is `'\0'` + * "%h" - Same as "%b" + * "%^s" - Same as "%s" but with an additional argument, as the first one: + * a `const char **` used to store the matched string + * "%^u" - Same as "%u" but with an additional argument, as the first one: + * a `uintmax_t *` used to store the encoded integer + * "%^p" - Same as "%p" but with an additional argument, as the first one: + * a `uintmax_t *` used to store the encoded integer + * "%^b" - Same as "%b" but with an additional argument, as the first one: + * a `uintmax_t *` used to store the number of encoded bytes or + * the encoded integer after the asterisk if asterisk-encoding is used + * "%&b" - Same as "%b" but with two additional arguments, as the first two: + * a `const char **` and a `uintmax_t *`: if asterisk-notation is used + * the `const char *` will be set to `NULL` and the `uintmax_t` will be + * set to the encoded number, othererwise the `const char *` will be + * set to point to the position in `settings` where the base-64 encoded + * text begins and the `uintmax_t` will be set to length of the text + * (as encoded in base-64, _not_ as decoded to binary) + * "%^h" - Same as "%^b" + * "%&h" - Same as "%&b" + * @return 1 if `settings` matches `fmt`, 0 otherwise + * + * This function will call abort(3) if misused. + * + * @seealso librecrypt_set_custom_algorithms + * + * @since 1.1 + */ +LIBRECRYPT_READ_MEM__(1, 2) LIBRECRYPT_NONNULL_I__(3) LIBRECRYPT_WUR__ +int librecrypt_scan_settings(const char *settings, size_t len, const char *fmt, ...); #if defined(__clang__) diff --git a/librecrypt_check_settings_.c b/librecrypt_check_settings_.c deleted file mode 100644 index 402b8b2..0000000 --- a/librecrypt_check_settings_.c +++ /dev/null @@ -1,799 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "common.h" -#ifndef TEST - - -/** - * Parse and validate an unsigned, decimal integer - * - * @param settings The password hash string - * @param off The current in `settings` where the integer - * begins; will be updated to its ends (one byte - * after the last character encoding the integer) - * @param len The number of bytes in `settings` - * @param min_first_digit '0' if leading zeroes are permitted, '1' otherwise - * @param min The smallest legal value - * @param max The greatest legal value - * @param out Output parameter for the encoded integer, or `NULL` - * @return 1 if a valid integer was encoded, 0 otherwise - */ -static int -check_uint(const char *settings, size_t *off, size_t len, char min_first_digit, - uintmax_t min, uintmax_t max, uintmax_t *out) -{ - uintmax_t digit, value; - - /* Reject the empty string */ - if (*off >= len) - return 0; - - /* Reject leading zeroes unless `min_first_digit` is '0' */ - if (min_first_digit > settings[*off] || settings[*off] > '9') { - /* With leading zeroes being rejected, a single '0' may be - * parsed as the value 0 (if that value is permitted) */ - if (min == 0u && min_first_digit != '0' && settings[*off] == '0') { - ++*off; - if (out) - *out = 0u; - return 1; - } - return 0; - } - - /* Decode the integer */ - value = (uintmax_t)(settings[*off] - '0'); - ++*off; - while (*off < len && '0' <= settings[*off] && settings[*off] <= '9') { - digit = (uintmax_t)(settings[*off] - '0'); - if (value > (UINTMAX_MAX - digit) / 10u) - return 0; - value *= 10u; - value += digit; - ++*off; - } - if (out) - *out = value; - - /* Check that the integer is within the accepted range */ - return min <= value && value <= max; -} - - -/** - * Validate and returns a salt or hash that's either encoded - * in base-64 or has its length encoded using asterisk-notation - * - * This function does not check the value of any excess bit - * in the base-64 encoding - * - * @param settings The password hash string - * @param off The current in `settings` where the integer - * begins; will be updated to its ends (one byte - * after the last character encoding the integer) - * @param len The number of bytes in `settings` - * @param min The least allowed number of bytes - * @param max The most allowed number of bytes - * @param allow_empty Whether the empty string is allowed - * (no encoded bytes and no asterisk-notation) - * @param dlut Alphabet reverse lookup table, shall map any valid - * character (except the padding character) to the value - * of that character in the encoding alphabet, and map - * any other character to the value `0xFF` - * @param pad The padding character to used at the end; the NUL byte if none - * @param strict_pad Zero if the padding at the end is optional, non-zero otherwise - * @param strout Output parameter for the beginning of the base-64 text, - * set to `NULL` if asterisk-notation is used - * @param lenout Output parameter for the number of bytes in `*strout`, or if - * `*strout` is set to `NULL`, the asterisk-encoded number; - * however if `strout` is `NULL`, the number bytes used by - * the salt or hash (when in raw binary format) is stored - * @return 1 if the encoded value was of proper length, - * a proper length was encoded using asterisk-notation, or - * if `allow_empty` was non-zero, nothing was encoded; - * 0 otherwise - */ -static int -check_data(const char *settings, size_t *off, size_t len, uintmax_t min, uintmax_t max, int allow_empty, - const unsigned char dlut[restrict static 256], char pad, int strict_pad, - const char **strout, uintmax_t *lenout) -{ - size_t i, old_i; - uintmax_t q, r, n; - - /* Check for asterisk-notation */ - if (*off < len && settings[*off] == '*') { - ++*off; - if (strout) - *strout = NULL; - return check_uint(settings, off, len, '0', min, max, lenout); - } - - settings = &settings[*off]; - len -= *off; - i = 0u; - - /* Get number of base-64 characters available, excluding padding */ - while (i < len && dlut[(unsigned char)settings[i]] < 64u) - i++; - - /* Calculate number of encoded bytes */ - q = i / 4u; - r = i % 4u; - n = q * 3u + r - (r ? 1u : 0u); - if (!strout && lenout) - *lenout = n; - /* 1 base-64 character in excees of a multiple of 4, - * this is illegal because 4 characters encode 3 bytes - * without any excees bits (both are 24 bits), so - * if the number of encoded bytes is not a multiple - * of 3, at least 2 characters in excees of multiple - * of 4 is required because at least 2 characeters is - * required to encode 1 byte in base-64 */ - if (r == 1u) - return 0; - - /* Check number of bytes is within the specified range, - * but accept 0 if `allow_empty` is non-zero */ - if (min > n || n > max) - if (n || !allow_empty) - return 0; - - /* Check padding if allowed or even required */ - if (r && pad) { - old_i = i; - while (r < 4u && i < len && settings[i] == pad) { - i++; - r++; - } - if ((i != old_i || strict_pad) && r != 4u) - return 0; - } - - /* Return the base-64 encoding */ - if (strout) { - *strout = settings; - *lenout = (uintmax_t)i; - } - - *off += i; - return 1; -} - - -int -librecrypt_scan_settings_(const char *settings, size_t len, const char *fmt, ...) -{ - size_t i = 0u; - uintmax_t *uout, umin, umax; - const unsigned char *dlut; - char pad; - int strict_pad; - int output = 0; - va_list args; - const char *str, **strout; - size_t n; - - va_start(args, fmt); - - while (*fmt) { - if (*fmt != '%') { - /* Normal literal character */ - if (i == len) - return 0; - if (settings[i++] != *fmt++) - return 0; - - } else if (fmt[1u] == '%') { - /* '%'-escaped literal '%' ("%%") */ - if (i == len) - return 0; - if (settings[i++] != '%') - return 0; - fmt = &fmt[2u]; - - } else if (fmt[1u] == '*') { - /* "%*": skip to next '$' or the end of no '$' was found */ - while (i < len && settings[i] != '$') - i++; - fmt = &fmt[2u]; - - } else if (fmt[1u] == '^') { - /* '^' inserted between '%' and some letter may be used - * have the value returned */ - output = 1; - fmt++; - goto outable; - - } else if (fmt[1u] == '&' && (fmt[2u] == 'b' || fmt[2u] == 'h')) { - /* Like "%b"/"%h" except output pointer to base-64 text or decodes asterisk-notation */ - strout = va_arg(args, const char **); - uout = va_arg(args, uintmax_t *); - fmt++; - goto plain_bh; - - } else outable: if (fmt[1u] == 'p' || fmt[1u] == 'u') { - /* Unsigned integers ("%p" for leading zeros forbidden, "%u" for leading zeros allowed) */ - uout = output ? va_arg(args, uintmax_t *) : NULL; - umin = va_arg(args, uintmax_t); - umax = va_arg(args, uintmax_t); - if (!check_uint(settings, &i, len, fmt[1u] == 'p' ? '1' : '0', umin, umax, uout)) - return 0; - goto outable_done; - - } else if (fmt[1u] == 'b' || fmt[1u] == 'h') { - /* Base-64 or asterisk-notation ("%b" for normal, "%h" for "" allowed) */ - strout = NULL; - uout = output ? va_arg(args, uintmax_t *) : NULL; - plain_bh: - umin = va_arg(args, uintmax_t); - umax = va_arg(args, uintmax_t); - dlut = va_arg(args, const unsigned char *); - pad = (char)va_arg(args, int); /* `char` is promoted to `int` when passed through `...` */ - strict_pad = va_arg(args, int); - if (!check_data(settings, &i, len, umin, umax, fmt[1u] == 'h', dlut, pad, strict_pad, strout, uout)) - return 0; - goto outable_done; - - } else if (fmt[1u] == 's') { - /* String from fixed set ("%s") */ - strout = output ? va_arg(args, const char **) : NULL; - for (;;) { - str = va_arg(args, const char *); - if (!str) - return 0; - n = strlen(str); - if (n > len - i) - continue; - if (strncmp(&settings[i], str, n)) - continue; - if (strout) - *strout = str; - i += n; - break; - } - while ((str = va_arg(args, const char *))); - goto outable_done; - - } else { - abort(); /* $covered$ */ - - outable_done: - output = 0; - fmt = &fmt[2u]; - } - } - - va_end(args); - - return i == len; -} - - - -#else - - -#define RANGE(A, B) (uintmax_t)(A), (uintmax_t)(B) -#define BASE64(A, B) RANGE(A, B), lut, pad, strict_pad - - -static unsigned char lut[256]; - - -static void -check_asterisk(char pad, int strict_pad) -{ - uintmax_t u, u2; - const char *s, *s2; - - EXPECT(librecrypt_scan_settings_("*012", 4u, "%b", BASE64(0, 100)) == 1); - EXPECT(librecrypt_scan_settings_("*200", 4u, "%b", BASE64(0, 100)) == 0); - EXPECT(librecrypt_scan_settings_("*012", 4u, "%b", BASE64(100, 200)) == 0); - EXPECT(librecrypt_scan_settings_("*0", 2u, "%b", BASE64(0, 100)) == 1); - EXPECT(librecrypt_scan_settings_("*00", 3u, "%b", BASE64(0, 100)) == 1); - EXPECT(librecrypt_scan_settings_("", 0u, "%b", BASE64(0, 100)) == 1); - EXPECT(librecrypt_scan_settings_("", 0u, "%b", BASE64(5, 100)) == 0); - - EXPECT(librecrypt_scan_settings_("*012.", 5u, "%b.", BASE64(0, 100)) == 1); - EXPECT(librecrypt_scan_settings_("*012-", 5u, "%b.", BASE64(0, 100)) == 0); - EXPECT(librecrypt_scan_settings_("*012", 4u, "%b.", BASE64(0, 100)) == 0); - - EXPECT(librecrypt_scan_settings_("*2*5", 4u, "%b%b", BASE64(2, 2), BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("*2*4", 4u, "%b%b", BASE64(2, 2), BASE64(5, 5)) == 0); - - EXPECT(librecrypt_scan_settings_("*012", 4u, "%h", BASE64(0, 100)) == 1); - EXPECT(librecrypt_scan_settings_("*200", 4u, "%h", BASE64(0, 100)) == 0); - EXPECT(librecrypt_scan_settings_("*012", 4u, "%h", BASE64(100, 200)) == 0); - EXPECT(librecrypt_scan_settings_("*0", 2u, "%h", BASE64(0, 100)) == 1); - EXPECT(librecrypt_scan_settings_("*00", 3u, "%h", BASE64(0, 100)) == 1); - EXPECT(librecrypt_scan_settings_("", 0u, "%h", BASE64(0, 100)) == 1); - EXPECT(librecrypt_scan_settings_("", 0u, "%h", BASE64(5, 100)) == 1); - - EXPECT(librecrypt_scan_settings_("*012.", 5u, "%h.", BASE64(0, 100)) == 1); - EXPECT(librecrypt_scan_settings_("*012-", 5u, "%h.", BASE64(0, 100)) == 0); - EXPECT(librecrypt_scan_settings_("*012", 4u, "%h.", BASE64(0, 100)) == 0); - - EXPECT(librecrypt_scan_settings_("*2*5", 4u, "%h%h", BASE64(2, 2), BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("*2*4", 4u, "%h%h", BASE64(2, 2), BASE64(5, 5)) == 0); - - u = 0u; - EXPECT(librecrypt_scan_settings_("*012", 4u, "%^b", &u, BASE64(0, 100)) == 1); - EXPECT(u == 12u); - EXPECT(librecrypt_scan_settings_("*200", 4u, "%^b", &u, BASE64(0, 100)) == 0); - EXPECT(librecrypt_scan_settings_("*012", 4u, "%^b", &u, BASE64(100, 200)) == 0); - u = 99u; - EXPECT(librecrypt_scan_settings_("*0", 2u, "%^b", &u, BASE64(0, 100)) == 1); - EXPECT(u == 0u); - u = 99u; - EXPECT(librecrypt_scan_settings_("*00", 3u, "%^b", &u, BASE64(0, 100)) == 1); - EXPECT(u == 0u); - u = 99u; - EXPECT(librecrypt_scan_settings_("", 0u, "%^b", &u, BASE64(0, 100)) == 1); - EXPECT(u == 0u); - EXPECT(librecrypt_scan_settings_("", 0u, "%^b", &u, BASE64(5, 100)) == 0); - - EXPECT(librecrypt_scan_settings_("*012.", 5u, "%^b.", &u, BASE64(0, 100)) == 1); - EXPECT(librecrypt_scan_settings_("*012-", 5u, "%^b.", &u, BASE64(0, 100)) == 0); - EXPECT(librecrypt_scan_settings_("*012", 4u, "%^b.", &u, BASE64(0, 100)) == 0); - - u = u2 = 99u; - EXPECT(librecrypt_scan_settings_("*2*5", 4u, "%^b%^b", &u, BASE64(2, 2), &u2, BASE64(5, 5)) == 1); - EXPECT(u == 2u); - EXPECT(u2 == 5u); - EXPECT(librecrypt_scan_settings_("*2*4", 4u, "%^b%^b", &u, BASE64(2, 2), &u2, BASE64(5, 5)) == 0); - - u = 0u; - EXPECT(librecrypt_scan_settings_("*012", 4u, "%^h", &u, BASE64(0, 100)) == 1); - EXPECT(u == 12u); - EXPECT(librecrypt_scan_settings_("*200", 4u, "%^h", &u, BASE64(0, 100)) == 0); - EXPECT(librecrypt_scan_settings_("*012", 4u, "%^h", &u, BASE64(100, 200)) == 0); - u = 99u; - EXPECT(librecrypt_scan_settings_("*0", 2u, "%^h", &u, BASE64(0, 100)) == 1); - EXPECT(u == 0u); - u = 99u; - EXPECT(librecrypt_scan_settings_("*00", 3u, "%^h", &u, BASE64(0, 100)) == 1); - EXPECT(u == 0u); - u = 99u; - EXPECT(librecrypt_scan_settings_("", 0u, "%^h", &u, BASE64(0, 100)) == 1); - EXPECT(u == 0u); - u = 99u; - EXPECT(librecrypt_scan_settings_("", 0u, "%^h", &u, BASE64(5, 100)) == 1); - EXPECT(u == 0u); - - EXPECT(librecrypt_scan_settings_("*012.", 5u, "%^h.", &u, BASE64(0, 100)) == 1); - EXPECT(librecrypt_scan_settings_("*012-", 5u, "%^h.", &u, BASE64(0, 100)) == 0); - EXPECT(librecrypt_scan_settings_("*012", 4u, "%^h.", &u, BASE64(0, 100)) == 0); - - u = u2 = 99u; - EXPECT(librecrypt_scan_settings_("*2*5", 4u, "%^h%^h", &u, BASE64(2, 2), &u2, BASE64(5, 5)) == 1); - EXPECT(u == 2u); - EXPECT(u2 == 5u); - EXPECT(librecrypt_scan_settings_("*2*4", 4u, "%^h%^h", &u, BASE64(2, 2), &u2, BASE64(5, 5)) == 0); - - s = ""; - u = 99u; - EXPECT(librecrypt_scan_settings_("*5.", 3, "%&b.", &s, &u, BASE64(2, 100)) == 1); - EXPECT(s == NULL); - EXPECT(u == 5u); - - s = ""; - u = 99u; - EXPECT(librecrypt_scan_settings_("*5", 2, "%&b", &s, &u, BASE64(2, 100)) == 1); - EXPECT(s == NULL); - EXPECT(u == 5u); - - EXPECT(librecrypt_scan_settings_("*5", 2, "%&b", &s, &u, BASE64(1, 4)) == 0); - EXPECT(librecrypt_scan_settings_("*5", 2, "%&b", &s, &u, BASE64(6, 9)) == 0); - - s = s2 = ""; - u = u2 = 99u; - EXPECT(librecrypt_scan_settings_("*5*10", 5, "%&b%&b", &s, &u, BASE64(2, 100), &s2, &u2, BASE64(2, 100)) == 1); - EXPECT(s == NULL); - EXPECT(u == 5u); - EXPECT(s2 == NULL); - EXPECT(u2 == 10u); - - EXPECT(librecrypt_scan_settings_("*", 1u, "%b", BASE64(0, 10)) == 0); - EXPECT(librecrypt_scan_settings_("*", 1u, "%h", BASE64(0, 10)) == 0); - EXPECT(librecrypt_scan_settings_("*", 1u, "%^b", &u, BASE64(0, 10)) == 0); - EXPECT(librecrypt_scan_settings_("*", 1u, "%^h", &u, BASE64(0, 10)) == 0); - EXPECT(librecrypt_scan_settings_("*", 1u, "%&b", &s, &u, BASE64(0, 10)) == 0); - EXPECT(librecrypt_scan_settings_("*", 1u, "%&h", &s, &u, BASE64(0, 10)) == 0); - EXPECT(librecrypt_scan_settings_("*x", 2u, "%b", BASE64(0, 10)) == 0); - EXPECT(librecrypt_scan_settings_("*x", 2u, "%h", BASE64(0, 10)) == 0); - EXPECT(librecrypt_scan_settings_("*x", 2u, "%^b", &u, BASE64(0, 10)) == 0); - EXPECT(librecrypt_scan_settings_("*x", 2u, "%^h", &u, BASE64(0, 10)) == 0); - EXPECT(librecrypt_scan_settings_("*x", 2u, "%&b", &s, &u, BASE64(0, 10)) == 0); - EXPECT(librecrypt_scan_settings_("*x", 2u, "%&h", &s, &u, BASE64(0, 10)) == 0); -} - - -static void -check_base64(char pad, int strict_pad) -{ - uintmax_t u; - const char *s; - - EXPECT(librecrypt_scan_settings_("", 0u, "%b", BASE64(0, 100)) == 1); - EXPECT(librecrypt_scan_settings_("", 0u, "%b", BASE64(1, 100)) == 0); - EXPECT(librecrypt_scan_settings_("abcd", 4u, "%b", BASE64(3, 3)) == 1); - EXPECT(librecrypt_scan_settings_("abcd", 4u, "%b", BASE64(4, 4)) == 0); - - EXPECT(librecrypt_scan_settings_("", 0u, "%h", BASE64(0, 100)) == 1); - EXPECT(librecrypt_scan_settings_("", 0u, "%h", BASE64(1, 100)) == 1); - EXPECT(librecrypt_scan_settings_("abcd", 4u, "%h", BASE64(3, 3)) == 1); - EXPECT(librecrypt_scan_settings_("abcd", 4u, "%h", BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcd", 4u, "%h", BASE64(2, 2)) == 0); - - u = 99u; - EXPECT(librecrypt_scan_settings_("", 0u, "%^b", &u, BASE64(0, 100)) == 1); - EXPECT(u == 0u); - EXPECT(librecrypt_scan_settings_("", 0u, "%^b", &u, BASE64(1, 100)) == 0); - u = 99u; - EXPECT(librecrypt_scan_settings_("abcd", 4u, "%^b", &u, BASE64(3, 3)) == 1); - EXPECT(u == 3u); - EXPECT(librecrypt_scan_settings_("abcd", 4u, "%^b", &u, BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcd", 4u, "%^b", &u, BASE64(2, 2)) == 0); - - u = 99u; - EXPECT(librecrypt_scan_settings_("", 0u, "%^h", &u, BASE64(0, 100)) == 1); - EXPECT(u == 0u); - u = 99u; - EXPECT(librecrypt_scan_settings_("", 0u, "%^h", &u, BASE64(1, 100)) == 1); - EXPECT(u == 0u); - u = 99u; - EXPECT(librecrypt_scan_settings_("abcd", 4u, "%^h", &u, BASE64(3, 3)) == 1); - EXPECT(u == 3u); - EXPECT(librecrypt_scan_settings_("abcd", 4u, "%^h", &u, BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcd", 4u, "%^h", &u, BASE64(2, 2)) == 0); - - s = NULL; - u = 99u; - EXPECT(librecrypt_scan_settings_("_", 1u, "_%&b", &s, &u, BASE64(0, 100)) == 1); - EXPECT(u == 0u); - EXPECT(s && !strcmp(s, "")); - EXPECT(librecrypt_scan_settings_("_", 1u, "_%&b", &s, &u, BASE64(1, 100)) == 0); - s = NULL; - u = 99u; - EXPECT(librecrypt_scan_settings_("_abcd", 5u, "_%&b", &s, &u, BASE64(3, 3)) == 1); - EXPECT(u == 4u); - EXPECT(s && !strcmp(s, "abcd")); - EXPECT(librecrypt_scan_settings_("_abcd", 5u, "_%&b", &s, &u, BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("_abcd", 5u, "_%&b", &s, &u, BASE64(2, 2)) == 0); - - s = NULL; - u = 99u; - EXPECT(librecrypt_scan_settings_("_", 1u, "_%&h", &s, &u, BASE64(0, 100)) == 1); - EXPECT(u == 0u); - EXPECT(s && !strcmp(s, "")); - s = NULL; - u = 99u; - EXPECT(librecrypt_scan_settings_("_", 1u, "_%&h", &s, &u, BASE64(1, 100)) == 1); - EXPECT(u == 0u); - EXPECT(s && !strcmp(s, "")); - s = NULL; - u = 99u; - EXPECT(librecrypt_scan_settings_("_abcd", 5u, "_%&h", &s, &u, BASE64(3, 3)) == 1); - EXPECT(u == 4u); - EXPECT(s && !strcmp(s, "abcd")); - EXPECT(librecrypt_scan_settings_("_abcd", 5u, "_%&h", &s, &u, BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("_abcd", 5u, "_%&h", &s, &u, BASE64(2, 2)) == 0); - - u = 99u; - EXPECT(librecrypt_scan_settings_("_abcd_", 6u, "_%&b_", &s, &u, BASE64(3, 3)) == 1); - EXPECT(u == 4u); - EXPECT(s && !strcmp(s, "abcd_")); - u = 99u; - EXPECT(librecrypt_scan_settings_("_abcd_", 6u, "_%&h_", &s, &u, BASE64(3, 3)) == 1); - EXPECT(u == 4u); - EXPECT(s && !strcmp(s, "abcd_")); - - u = 99u; - EXPECT(librecrypt_scan_settings_("abcdabcd", 8u, "%&b", &s, &u, BASE64(6, 6)) == 1); - EXPECT(u == 8u); - EXPECT(s && strlen(s) == u); - - u = 99u; - EXPECT(librecrypt_scan_settings_("abcdabcd", 8u, "%&h", &s, &u, BASE64(6, 6)) == 1); - EXPECT(u == 8u); - EXPECT(s && strlen(s) == u); - - EXPECT(librecrypt_scan_settings_("abcda", 5u, "%&b", &s, &u, BASE64(1, 10)) == 0); - EXPECT(librecrypt_scan_settings_("abcda---", 8u, "%&b", &s, &u, BASE64(1, 10)) == 0); - EXPECT(librecrypt_scan_settings_("abcda---", 8u, "%&b---", &s, &u, BASE64(1, 10)) == 0); - EXPECT(librecrypt_scan_settings_("abcda", 5u, "%&h", &s, &u, BASE64(1, 10)) == 0); - EXPECT(librecrypt_scan_settings_("abcda---", 8u, "%&h", &s, &u, BASE64(1, 10)) == 0); - EXPECT(librecrypt_scan_settings_("abcda---", 8u, "%&h---", &s, &u, BASE64(1, 10)) == 0); - - if (pad && strict_pad) { - EXPECT(librecrypt_scan_settings_("abcdab", 6u, "%&b", &s, &u, BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdab", 6u, "%&h", &s, &u, BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc", 7u, "%&b", &s, &u, BASE64(5, 5)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc", 7u, "%&h", &s, &u, BASE64(5, 5)) == 0); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%&b", &s, &u, BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%&h", &s, &u, BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%&b", &s, &u, BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%&h", &s, &u, BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%&b--", &s, &u, BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%&h--", &s, &u, BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%&b-", &s, &u, BASE64(5, 5)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%&h-", &s, &u, BASE64(5, 5)) == 0); - } else if (pad) { - EXPECT(librecrypt_scan_settings_("abcdab", 6u, "%&b", &s, &u, BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab", 6u, "%&h", &s, &u, BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc", 7u, "%&b", &s, &u, BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc", 7u, "%&h", &s, &u, BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%&b", &s, &u, BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%&h", &s, &u, BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%&b", &s, &u, BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%&h", &s, &u, BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%&b--", &s, &u, BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%&h--", &s, &u, BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%&b-", &s, &u, BASE64(5, 5)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%&h-", &s, &u, BASE64(5, 5)) == 0); - } else { - EXPECT(librecrypt_scan_settings_("abcdab", 6u, "%&b", &s, &u, BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab", 6u, "%&h", &s, &u, BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc", 7u, "%&b", &s, &u, BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc", 7u, "%&h", &s, &u, BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%&b", &s, &u, BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%&h", &s, &u, BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%&b", &s, &u, BASE64(5, 5)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%&h", &s, &u, BASE64(5, 5)) == 0); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%&b--", &s, &u, BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%&h--", &s, &u, BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%&b-", &s, &u, BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%&h-", &s, &u, BASE64(5, 5)) == 1); - } - - if (pad && strict_pad) { - EXPECT(librecrypt_scan_settings_("abcdab", 6u, "%^b", &u, BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdab", 6u, "%^h", &u, BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc", 7u, "%^b", &u, BASE64(5, 5)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc", 7u, "%^h", &u, BASE64(5, 5)) == 0); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%^b", &u, BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%^h", &u, BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%^b", &u, BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%^h", &u, BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%^b--", &u, BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%^h--", &u, BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%^b-", &u, BASE64(5, 5)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%^h-", &u, BASE64(5, 5)) == 0); - } else if (pad) { - EXPECT(librecrypt_scan_settings_("abcdab", 6u, "%^b", &u, BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab", 6u, "%^h", &u, BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc", 7u, "%^b", &u, BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc", 7u, "%^h", &u, BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%^b", &u, BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%^h", &u, BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%^b", &u, BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%^h", &u, BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%^b--", &u, BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%^h--", &u, BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%^b-", &u, BASE64(5, 5)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%^h-", &u, BASE64(5, 5)) == 0); - } else { - EXPECT(librecrypt_scan_settings_("abcdab", 6u, "%^b", &u, BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab", 6u, "%^h", &u, BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc", 7u, "%^b", &u, BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc", 7u, "%^h", &u, BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%^b", &u, BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%^h", &u, BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%^b", &u, BASE64(5, 5)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%^h", &u, BASE64(5, 5)) == 0); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%^b--", &u, BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%^h--", &u, BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%^b-", &u, BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%^h-", &u, BASE64(5, 5)) == 1); - } - - if (pad && strict_pad) { - EXPECT(librecrypt_scan_settings_("abcdab", 6u, "%b", BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdab", 6u, "%h", BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc", 7u, "%b", BASE64(5, 5)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc", 7u, "%h", BASE64(5, 5)) == 0); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%b", BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%h", BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%b", BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%h", BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%b--", BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%h--", BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%b-", BASE64(5, 5)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%h-", BASE64(5, 5)) == 0); - } else if (pad) { - EXPECT(librecrypt_scan_settings_("abcdab", 6u, "%b", BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab", 6u, "%h", BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc", 7u, "%b", BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc", 7u, "%h", BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%b", BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%h", BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%b", BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%h", BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%b--", BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%h--", BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%b-", BASE64(5, 5)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%h-", BASE64(5, 5)) == 0); - } else { - EXPECT(librecrypt_scan_settings_("abcdab", 6u, "%b", BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab", 6u, "%h", BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc", 7u, "%b", BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc", 7u, "%h", BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%b", BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%h", BASE64(4, 4)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%b", BASE64(5, 5)) == 0); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%h", BASE64(5, 5)) == 0); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%b--", BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdab--", 8u, "%h--", BASE64(4, 4)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%b-", BASE64(5, 5)) == 1); - EXPECT(librecrypt_scan_settings_("abcdabc-", 8u, "%h-", BASE64(5, 5)) == 1); - } -} - - -static int discarded_int; - -int -main(void) -{ - const char *s, *s2; - uintmax_t u, u2; - - SET_UP_ALARM(); - INIT_TEST_ABORT(); - INIT_RESOURCE_TEST(); - - memset(lut, 255, sizeof(lut)); - lut['a'] = lut['b'] = lut['c'] = lut['d'] = 0; - - EXPECT(librecrypt_scan_settings_("", 0u, "") == 1); - EXPECT(librecrypt_scan_settings_("hej", 3u, "hej") == 1); - EXPECT(librecrypt_scan_settings_("hej", 2u, "hej") == 0); - EXPECT(librecrypt_scan_settings_("hej", 4u, "hej") == 0); - EXPECT(librecrypt_scan_settings_("tja", 3u, "hej") == 0); - - EXPECT(librecrypt_scan_settings_("%", 1u, "%%") == 1); - EXPECT(librecrypt_scan_settings_("%", 0u, "%%") == 0); - EXPECT(librecrypt_scan_settings_("x", 1u, "%%") == 0); - - EXPECT(librecrypt_scan_settings_("hello", 5u, "%*") == 1); - EXPECT(librecrypt_scan_settings_("hello$world", 11u, "%*$world") == 1); - EXPECT(librecrypt_scan_settings_("hello$world", 11u, "%*$WORLD") == 0); - - EXPECT(librecrypt_scan_settings_("hej.", 4u, "%s.", "hej", "tja", NULL) == 1); - EXPECT(librecrypt_scan_settings_("tja.", 4u, "%s.", "hej", "tja", NULL) == 1); - EXPECT(librecrypt_scan_settings_("bye.", 4u, "%s.", "hej", "tja", NULL) == 0); - EXPECT(librecrypt_scan_settings_("hej-", 4u, "%s.", "hej", "tja", NULL) == 0); - EXPECT(librecrypt_scan_settings_("tja-", 4u, "%s.", "hej", "tja", NULL) == 0); - EXPECT(librecrypt_scan_settings_("bye-", 4u, "%s.", "hej", "tja", NULL) == 0); - EXPECT(librecrypt_scan_settings_("hej.", 3u, "%s.", "hej", "tja", NULL) == 0); - EXPECT(librecrypt_scan_settings_("tja.", 3u, "%s.", "hej", "tja", NULL) == 0); - EXPECT(librecrypt_scan_settings_("bye.", 3u, "%s.", "hej", "tja", NULL) == 0); - EXPECT(librecrypt_scan_settings_("hej.", 3u, "%s", "hej", "tja", NULL) == 1); - EXPECT(librecrypt_scan_settings_("tja.", 3u, "%s", "hej", "tja", NULL) == 1); - EXPECT(librecrypt_scan_settings_("bye.", 3u, "%s", "hej", "tja", NULL) == 0); - EXPECT(librecrypt_scan_settings_("hej.", 4u, "%s", "hej", "tja", NULL) == 0); - EXPECT(librecrypt_scan_settings_("tja.", 4u, "%s", "hej", "tja", NULL) == 0); - EXPECT(librecrypt_scan_settings_("bye.", 4u, "%s", "hej", "tja", NULL) == 0); - EXPECT(librecrypt_scan_settings_(".", 1u, "%s.", "hej", "tja", "", NULL) == 1); - EXPECT(librecrypt_scan_settings_("tja.", 4u, "%s.", "hej", "tja", "", NULL) == 1); - EXPECT(librecrypt_scan_settings_("tja.", 4u, "%s.", "", "hej", "tja", NULL) == 0); - EXPECT(librecrypt_scan_settings_("tja.", 4u, "%s.", NULL) == 0); - - EXPECT(librecrypt_scan_settings_("hejsan", 1u, "%s", "hej", NULL) == 0); - EXPECT(librecrypt_scan_settings_("hejsan", 3u, "%s", "hej", NULL) == 1); - EXPECT(librecrypt_scan_settings_("hejsan", 6u, "%s", "hej", NULL) == 0); - - s = NULL; - EXPECT(librecrypt_scan_settings_("hej.", 4u, "%^s.", &s, "hej", "tja", "", NULL) == 1); - EXPECT(s && !strcmp(s, "hej")); - s = NULL; - EXPECT(librecrypt_scan_settings_("tja.", 4u, "%^s.", &s, "hej", "tja", "", NULL) == 1); - EXPECT(s && !strcmp(s, "tja")); - s = NULL; - EXPECT(librecrypt_scan_settings_(".", 1u, "%^s.", &s, "hej", "tja", "", NULL) == 1); - EXPECT(s && !strcmp(s, "")); - - EXPECT(librecrypt_scan_settings_("10.", 3u, "%p.", RANGE(1, 10)) == 1); - EXPECT(librecrypt_scan_settings_("10.", 3u, "%p.", RANGE(10, 20)) == 1); - EXPECT(librecrypt_scan_settings_("10.", 3u, "%p.", RANGE(1, 20)) == 1); - EXPECT(librecrypt_scan_settings_("10.", 3u, "%p.", RANGE(1, 9)) == 0); - EXPECT(librecrypt_scan_settings_("10.", 3u, "%p.", RANGE(11, 20)) == 0); - EXPECT(librecrypt_scan_settings_("0.", 2u, "%p.", RANGE(1, 10)) == 0); - EXPECT(librecrypt_scan_settings_("0.", 2u, "%p.", RANGE(0, 10)) == 1); - EXPECT(librecrypt_scan_settings_("00.", 3u, "%p.", RANGE(0, 10)) == 0); - EXPECT(librecrypt_scan_settings_("010.", 4u, "%p.", RANGE(1, 10)) == 0); - - EXPECT(librecrypt_scan_settings_("10.", 3u, "%u.", RANGE(1, 10)) == 1); - EXPECT(librecrypt_scan_settings_("10.", 3u, "%u.", RANGE(10, 20)) == 1); - EXPECT(librecrypt_scan_settings_("10.", 3u, "%u.", RANGE(1, 20)) == 1); - EXPECT(librecrypt_scan_settings_("10.", 3u, "%u.", RANGE(1, 9)) == 0); - EXPECT(librecrypt_scan_settings_("10.", 3u, "%u.", RANGE(11, 20)) == 0); - EXPECT(librecrypt_scan_settings_("0.", 2u, "%u.", RANGE(1, 10)) == 0); - EXPECT(librecrypt_scan_settings_("0.", 2u, "%u.", RANGE(0, 10)) == 1); - EXPECT(librecrypt_scan_settings_("00.", 3u, "%u.", RANGE(0, 10)) == 1); - EXPECT(librecrypt_scan_settings_("010.", 4u, "%u.", RANGE(1, 10)) == 1); - - u = 99u; - EXPECT(librecrypt_scan_settings_("10.", 3u, "%^p.", &u, RANGE(0, 100)) == 1); - EXPECT(u == 10u); - EXPECT(librecrypt_scan_settings_("15.", 3u, "%^p.", &u, RANGE(0, 100)) == 1); - EXPECT(u == 15u); - EXPECT(librecrypt_scan_settings_("0.", 2u, "%^p.", &u, RANGE(0, 100)) == 1); - EXPECT(u == 0u); - - u = 99u; - EXPECT(librecrypt_scan_settings_("10.", 3u, "%^u.", &u, RANGE(0, 100)) == 1); - EXPECT(u == 10u); - EXPECT(librecrypt_scan_settings_("15.", 3u, "%^u.", &u, RANGE(0, 100)) == 1); - EXPECT(u == 15u); - EXPECT(librecrypt_scan_settings_("0.", 2u, "%^u.", &u, RANGE(0, 100)) == 1); - EXPECT(u == 0u); - EXPECT(librecrypt_scan_settings_("010.", 4u, "%^u.", &u, RANGE(0, 100)) == 1); - EXPECT(u == 10u); - EXPECT(librecrypt_scan_settings_("015.", 4u, "%^u.", &u, RANGE(0, 100)) == 1); - EXPECT(u == 15u); - EXPECT(librecrypt_scan_settings_("00.", 3u, "%^u.", &u, RANGE(0, 100)) == 1); - EXPECT(u == 0u); - - EXPECT(librecrypt_scan_settings_("10.15.", 6u, "%u.%u.", RANGE(1, 10), RANGE(11, 20)) == 1); - EXPECT(librecrypt_scan_settings_("10.10.", 6u, "%u.%u.", RANGE(1, 10), RANGE(11, 20)) == 0); - EXPECT(librecrypt_scan_settings_("15.10.", 6u, "%u.%u.", RANGE(1, 10), RANGE(11, 20)) == 0); - - u = 99u; - u2 = 99u; - EXPECT(librecrypt_scan_settings_("10.15.", 6u, "%^u.%^u.", &u, RANGE(1, 10), &u2, RANGE(11, 20)) == 1); - EXPECT(u == 10u); - EXPECT(u2 == 15u); - - EXPECT(librecrypt_scan_settings_("", 0u, "%p", RANGE(0, 10)) == 0); - EXPECT(librecrypt_scan_settings_("a", 1u, "%p", RANGE(0, 10)) == 0); - EXPECT(librecrypt_scan_settings_("", 0u, "%u", RANGE(0, 10)) == 0); - EXPECT(librecrypt_scan_settings_("a", 1u, "%u", RANGE(0, 10)) == 0); - EXPECT(librecrypt_scan_settings_("", 0u, "%^p", &u, RANGE(0, 10)) == 0); - EXPECT(librecrypt_scan_settings_("a", 1u, "%^p", &u, RANGE(0, 10)) == 0); - EXPECT(librecrypt_scan_settings_("", 0u, "%^u", &u, RANGE(0, 10)) == 0); - EXPECT(librecrypt_scan_settings_("a", 1u, "%^u", &u, RANGE(0, 10)) == 0); - - EXPECT(librecrypt_scan_settings_("hej.hello.", 10u, "%s.%s.", "hej", NULL, "hello", NULL) == 1); - EXPECT(librecrypt_scan_settings_("hello.hej.", 10u, "%s.%s.", "hej", NULL, "hello", NULL) == 0); - - EXPECT(librecrypt_scan_settings_("hejsan", 1u, "%^s", &s, "hej", NULL) == 0); - s = NULL; - EXPECT(librecrypt_scan_settings_("hejsan", 3u, "%^s", &s, "hej", NULL) == 1); - EXPECT(s && !strcmp(s, "hej")); - EXPECT(librecrypt_scan_settings_("hejsan", 6u, "%^s", &s, "hej", NULL) == 0); - - s = NULL; - s2 = NULL; - EXPECT(librecrypt_scan_settings_("hej.hello.", 10u, "%^s.%^s.", &s, "x", "hej", NULL, &s2, "y", "hello", NULL) == 1); - EXPECT(s && !strcmp(s, "hej")); - EXPECT(s2 && !strcmp(s2, "hello")); - - s = NULL; - s2 = NULL; - EXPECT(librecrypt_scan_settings_("x.y.", 4u, "%^s.%^s.", &s, "x", "hej", NULL, &s2, "y", "hello", NULL) == 1); - EXPECT(s && !strcmp(s, "x")); - EXPECT(s2 && !strcmp(s2, "y")); - - check_asterisk('-', 0); - check_asterisk('-', 1); - check_asterisk('\0', 0); - check_asterisk('\0', 1); - - check_base64('-', 0); - check_base64('-', 1); - check_base64('\0', 0); - check_base64('\0', 1); - -#define S(STR) (STR), (sizeof(STR) - 1u) -#define LARGE "999999999999999999999999999999999999999" - EXPECT(librecrypt_scan_settings_(S(LARGE LARGE LARGE LARGE), "%p", RANGE(0, UINTMAX_MAX)) == 0); - - EXPECT_ABORT(discarded_int = librecrypt_scan_settings_("", 0u, "%\xFF", 0, 0, 0, 0, 0, 0, 0)); - - STOP_RESOURCE_TEST(); - return 0; -} - - -#endif diff --git a/librecrypt_context_get_pepper_.c b/librecrypt_context_get_pepper_.c deleted file mode 100644 index 69373f5..0000000 --- a/librecrypt_context_get_pepper_.c +++ /dev/null @@ -1,63 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "common.h" -#ifndef TEST - - -struct pepper * -librecrypt_context_get_pepper_(LIBRECRYPT_CONTEXT *ctx, enum librecrypt_hash_algorithm algo, size_t len) -{ - struct pepper *pepper; - size_t index = (size_t)algo; - - switch (algo) { - -#if defined(SUPPORT_ARGON2I) || defined(SUPPORT_ARGON2D) || defined(SUPPORT_ARGON2ID) || defined(SUPPORT_ARGON2DS) -# if defined(SUPPORT_ARGON2I) - case LIBRECRYPT_ARGON2I_V1_0: - case LIBRECRYPT_ARGON2I_V1_3: -# endif -# if defined(SUPPORT_ARGON2D) - case LIBRECRYPT_ARGON2D_V1_0: - case LIBRECRYPT_ARGON2D_V1_3: -# endif -# if defined(SUPPORT_ARGON2ID) - case LIBRECRYPT_ARGON2ID_V1_0: - case LIBRECRYPT_ARGON2ID_V1_3: -# endif -# if defined(SUPPORT_ARGON2DS) - case LIBRECRYPT_ARGON2DS_V1_0: - case LIBRECRYPT_ARGON2DS_V1_3: -# endif -# if SIZE_MAX > UINT32_MAX /* LIBAR2_MAX_KEYLEN is just UINT32_MAX cast to size_t; keep it simple: don't include */ - if (len > UINT32_MAX) { - errno = EINVAL; - return NULL; - } -# endif - return &ctx->peppers[algo]; -#endif - - default: - errno = ENOSYS; - return NULL; - } -} - - -#else - - -int -main(void) -{ - SET_UP_ALARM(); - INIT_RESOURCE_TEST(); - - /* TODO test */ - - STOP_RESOURCE_TEST(); - return 0; -} - - -#endif diff --git a/librecrypt_context_get_user_data.c b/librecrypt_context_get_user_data.c deleted file mode 100644 index b49b42d..0000000 --- a/librecrypt_context_get_user_data.c +++ /dev/null @@ -1,29 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "common.h" -#ifndef TEST - - -void * -librecrypt_context_get_user_data(LIBRECRYPT_CONTEXT *ctx) -{ - return ctx->user_data; -} - - -#else - - -int -main(void) -{ - SET_UP_ALARM(); - INIT_RESOURCE_TEST(); - - /* TODO test */ - - STOP_RESOURCE_TEST(); - return 0; -} - - -#endif diff --git a/librecrypt_context_set_pepper.c b/librecrypt_context_set_pepper.c deleted file mode 100644 index a5d6151..0000000 --- a/librecrypt_context_set_pepper.c +++ /dev/null @@ -1,37 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "common.h" -#ifndef TEST - - -int -librecrypt_context_set_pepper(LIBRECRYPT_CONTEXT *ctx, enum librecrypt_hash_algorithm algo, const void *data, size_t len) -{ - struct pepper *pepper; - - pepper = librecrypt_context_get_pepper_(ctx, algo, len); - if (!pepper) - return -1; - - pepper->data = data; - pepper->len = len; - return 0; -} - - -#else - - -int -main(void) -{ - SET_UP_ALARM(); - INIT_RESOURCE_TEST(); - - /* TODO test */ - - STOP_RESOURCE_TEST(); - return 0; -} - - -#endif diff --git a/librecrypt_context_set_user_data.c b/librecrypt_context_set_user_data.c deleted file mode 100644 index f1be75f..0000000 --- a/librecrypt_context_set_user_data.c +++ /dev/null @@ -1,29 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "common.h" -#ifndef TEST - - -void -librecrypt_context_set_user_data(LIBRECRYPT_CONTEXT *ctx, void *user) -{ - ctx->user_data = user; -} - - -#else - - -int -main(void) -{ - SET_UP_ALARM(); - INIT_RESOURCE_TEST(); - - /* TODO test */ - - STOP_RESOURCE_TEST(); - return 0; -} - - -#endif diff --git a/librecrypt_create_context.3 b/librecrypt_create_context.3 new file mode 100644 index 0000000..cbff3ae --- /dev/null +++ b/librecrypt_create_context.3 @@ -0,0 +1,76 @@ +.TH LIBRECRYPT_CREATE_CONTEXT 3 LIBRECRYPT +.SH NAME +librecrypt_create_context - Create a library configuration object + +.SH SYNOPSIS +.nf +#include + +typedef struct librecrypt_context LIBRECRYPT_CONTEXT; + +LIBRECRYPT_CONTEXT *\fBlibrecrypt_create_context\fP(void); +.fi +.PP +Link with +.IR -lrecrypt . + +.SH DESCRIPTION +The +.BR librecrypt_create_context () +function allocates, and initialises, a new +library configuration object of the opaque type +.IR LIBRECRYPT_CONTEXT , +which is also an incomplete type in the header file, +as it's size and layout may differ depending on +version and implementation of the library. + +.SH RETURN VALUES +The +.BR librecrypt_create_context () +function returns a new pointer upon successful +completion, when no longer needed, it should be +deallocated using the +.BR librecrypt_free_context (3) +function. On failure +.I NULL +is returned and +.I errno +is set to describe the error. + +.SH ERRORS +The +.BR librecrypt_create_context () +function will fail if: +.TP +.B ENOMEM +Failed to allocate enough memory. + +.SH ATTRIBUTES +For an explanation of the terms used in this section, see +.BR attributes (7). +.PP +.TS +allbox; +lb lb lb +l l l. +Interface Attribute Value +T{ +.BR librecrypt_create_context () +T} Thread safety MT-Safe +T{ +.BR librecrypt_create_context() +T} Async-signal safety AS-Unsafe +.TE +.sp + +.SH HISTORY +The +.BR librecrypt_create_context () +function was introduced in version 1.1 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_free_context (3), +.BR librecrypt_set_pepper (3), +.BR librecrypt_set_custom_algorithms (3) diff --git a/librecrypt_create_context.c b/librecrypt_create_context.c index c9696d7..a6f5153 100644 --- a/librecrypt_create_context.c +++ b/librecrypt_create_context.c @@ -21,6 +21,7 @@ librecrypt_create_context(void) * (zeroed) associated with them */ ret->user_data = NULL; + ret->algos = NULL; for (i = 0u; i < ELEMSOF(ret->peppers); i++) ret->peppers[i].data = NULL; @@ -31,13 +32,27 @@ librecrypt_create_context(void) #else +extern LIBRECRYPT_CONTEXT *volatile ctx; +LIBRECRYPT_CONTEXT *volatile ctx; + + int main(void) { SET_UP_ALARM(); INIT_RESOURCE_TEST(); - /* TODO test */ + ctx = librecrypt_create_context(); + assert(ctx != NULL); + librecrypt_free_context(ctx); + + if (libtest_have_custom_malloc()) { + libtest_set_alloc_failure_in(1u); + errno = 0; + EXPECT(librecrypt_create_context() == NULL); + EXPECT(errno == ENOMEM); + EXPECT(libtest_get_alloc_failure_in() == 0u); + } STOP_RESOURCE_TEST(); return 0; diff --git a/librecrypt_crypt.3 b/librecrypt_crypt.3 index c7cba43..f9579e1 100644 --- a/librecrypt_crypt.3 +++ b/librecrypt_crypt.3 @@ -7,8 +7,8 @@ librecrypt_crypt - Compute password hash encoded in ASCII with settings prefix #include ssize_t \fBlibrecrypt_crypt\fP(char *restrict \fIout_buffer\fP, size_t \fIsize\fP, - const char *\fIphrase\fP, size_t \fIlen\fP, - const char *\fIsettings\fP, LIBRECRYPT_CONTEXT *\fIctx\fP); + const char *\fIphrase\fP, size_t \fIlen\fP, + const char *\fIsettings\fP, LIBRECRYPT_CONTEXT *\fIctx\fP); .fi .PP Link with diff --git a/librecrypt_find_first_algorithm_.c b/librecrypt_find_first_algorithm_.c index ebe3699..ff967c9 100644 --- a/librecrypt_find_first_algorithm_.c +++ b/librecrypt_find_first_algorithm_.c @@ -10,8 +10,22 @@ librecrypt_find_first_algorithm_(const char *settings, size_t len, LIBRECRYPT_CO const struct librecrypt_algorithm *algo, *found = NULL; size_t i; - (void) ctx; /* TODO */ + /* Search application provided hash functions first; + * they should have priority of they report the same + * priority as library-provided hash functions */ + if (!ctx) + goto library_provided; + /* TODO test */ + for (i = 0u; i < ctx->nalgos; i++) { + algo = &ctx->algos[i]; + r = (*algo->is_algorithm)(settings, len); + if (r > priority) { + priority = r; + found = algo; + } + } +library_provided: for (i = 0u;; i++) { /* Get next algorithm in the list */ algo = &librecrypt_algorithms_[i]; diff --git a/librecrypt_free_context.3 b/librecrypt_free_context.3 new file mode 100644 index 0000000..3741dfd --- /dev/null +++ b/librecrypt_free_context.3 @@ -0,0 +1,55 @@ +.TH LIBRECRYPT_FREE_CONTEXT 3 LIBRECRYPT +.SH NAME +librecrypt_free_context - Deallocate a library configuration object + +.SH SYNOPSIS +.nf +#include + +\fBlibrecrypt_free_context\fP(LIBRECRYPT_CONTEXT *\fIctx\fP); +.fi +.PP +Link with +.IR -lrecrypt . + +.SH DESCRIPTION +The +.BR librecrypt_free_context () +functions deallocates +.IR ctx . + +.SH RETURN VALUES +None. + +.SH ERRORS +The +.BR librecrypt_free_context () +function cannot fail. + +.SH ATTRIBUTES +For an explanation of the terms used in this section, see +.BR attributes (7). +.PP +.TS +allbox; +lb lb lb +l l l. +Interface Attribute Value +T{ +.BR librecrypt_free_context () +T} Thread safety MT-Safe +T{ +.BR librecrypt_free_context() +T} Async-signal safety AS-Unsafe +.TE +.sp + +.SH HISTORY +The +.BR librecrypt_free_context () +function was introduced in version 1.1 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_create_context (3) diff --git a/librecrypt_free_context.c b/librecrypt_free_context.c index 481282f..b2a62c9 100644 --- a/librecrypt_free_context.c +++ b/librecrypt_free_context.c @@ -6,6 +6,8 @@ void librecrypt_free_context(LIBRECRYPT_CONTEXT *ctx) { + if (!ctx) + return; librecrypt_wipe(ctx, sizeof(*ctx)); free(ctx); } @@ -14,15 +16,11 @@ librecrypt_free_context(LIBRECRYPT_CONTEXT *ctx) #else -int +CONST int main(void) { - SET_UP_ALARM(); - INIT_RESOURCE_TEST(); - - /* TODO test */ - - STOP_RESOURCE_TEST(); + /* Tested in other files */ + librecrypt_free_context(NULL); return 0; } diff --git a/librecrypt_get_pepper_.c b/librecrypt_get_pepper_.c new file mode 100644 index 0000000..df2a82d --- /dev/null +++ b/librecrypt_get_pepper_.c @@ -0,0 +1,152 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +#if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wswitch-enum" +#endif + + +struct pepper * +librecrypt_get_pepper_(LIBRECRYPT_CONTEXT *ctx, enum librecrypt_hash_algorithm algo, size_t len) +{ + switch (algo) { + +#if defined(SUPPORT_ARGON2I) || defined(SUPPORT_ARGON2D) || defined(SUPPORT_ARGON2ID) || defined(SUPPORT_ARGON2DS) +# if defined(SUPPORT_ARGON2I) + case LIBRECRYPT_ARGON2I_V1_0: + case LIBRECRYPT_ARGON2I_V1_3: +# endif +# if defined(SUPPORT_ARGON2D) + case LIBRECRYPT_ARGON2D_V1_0: + case LIBRECRYPT_ARGON2D_V1_3: +# endif +# if defined(SUPPORT_ARGON2ID) + case LIBRECRYPT_ARGON2ID_V1_0: + case LIBRECRYPT_ARGON2ID_V1_3: +# endif +# if defined(SUPPORT_ARGON2DS) + case LIBRECRYPT_ARGON2DS_V1_0: + case LIBRECRYPT_ARGON2DS_V1_3: +# endif +# if SIZE_MAX > UINT32_MAX /* LIBAR2_MAX_KEYLEN is just UINT32_MAX cast to size_t; keep it simple: don't include */ + if (len > UINT32_MAX) { + errno = EINVAL; + return NULL; + } +# endif + return &ctx->peppers[algo]; +#endif + + default: + errno = ENOSYS; + return NULL; + } +} + + +#else + + +#define CHECK_DISABLED(ALGO)\ + do {\ + errno = 0;\ + EXPECT(librecrypt_get_pepper_(ctx, ALGO, 0u) == NULL);\ + EXPECT(errno == ENOSYS);\ + errno = 0;\ + EXPECT(librecrypt_get_pepper_(ctx, ALGO, 1u) == NULL);\ + EXPECT(errno == ENOSYS);\ + errno = 0;\ + EXPECT(librecrypt_get_pepper_(ctx, ALGO, 64u) == NULL);\ + EXPECT(errno == ENOSYS);\ + errno = 0;\ + EXPECT(librecrypt_get_pepper_(ctx, ALGO, SIZE_MAX) == NULL);\ + EXPECT(errno == ENOSYS);\ + } while (0) + +#define CHECK_ARGON2_(ALGO)\ + do {\ + pepper = librecrypt_get_pepper_(ctx, ALGO, 0u);\ + EXPECT(pepper != NULL);\ + EXPECT(pepper->data == NULL);\ + EXPECT(pepper->len == 0u);\ + pepper = librecrypt_get_pepper_(ctx, ALGO, 1u);\ + EXPECT(pepper != NULL);\ + EXPECT(pepper->data == NULL);\ + EXPECT(pepper->len == 0u);\ + pepper = librecrypt_get_pepper_(ctx, ALGO, UINT32_MAX);\ + EXPECT(pepper != NULL);\ + EXPECT(pepper->data == NULL);\ + EXPECT(pepper->len == 0u);\ + } while (0) + +#if SIZE_MAX > UINT32_MAX +# define CHECK_ARGON2(ALGO)\ + do {\ + CHECK_ARGON2_(ALGO);\ + errno = 0;\ + EXPECT(librecrypt_get_pepper_(ctx, ALGO, (size_t)UINT32_MAX + 1u) == NULL);\ + EXPECT(errno == EINVAL);\ + } while (0) +#else +# define CHECK_ARGON2(ALGO) CHECK_ARGON2_(ALGO) +#endif + + +int +main(void) +{ + LIBRECRYPT_CONTEXT *ctx; + struct pepper *pepper; + + SET_UP_ALARM(); + INIT_RESOURCE_TEST(); + + ctx = librecrypt_create_context(); + assert(ctx != NULL); + +#if defined(SUPPORT_ARGON2I) + CHECK_ARGON2(LIBRECRYPT_ARGON2I_V1_0); + CHECK_ARGON2(LIBRECRYPT_ARGON2I_V1_3); +#else + CHECK_DISABLED(LIBRECRYPT_ARGON2I_V1_0); + CHECK_DISABLED(LIBRECRYPT_ARGON2I_V1_3); +#endif + +#if defined(SUPPORT_ARGON2D) + CHECK_ARGON2(LIBRECRYPT_ARGON2D_V1_0); + CHECK_ARGON2(LIBRECRYPT_ARGON2D_V1_3); +#else + CHECK_DISABLED(LIBRECRYPT_ARGON2D_V1_0); + CHECK_DISABLED(LIBRECRYPT_ARGON2D_V1_3); +#endif + +#if defined(SUPPORT_ARGON2ID) + CHECK_ARGON2(LIBRECRYPT_ARGON2ID_V1_0); + CHECK_ARGON2(LIBRECRYPT_ARGON2ID_V1_3); +#else + CHECK_DISABLED(LIBRECRYPT_ARGON2ID_V1_0); + CHECK_DISABLED(LIBRECRYPT_ARGON2ID_V1_3); +#endif + +#if defined(SUPPORT_ARGON2DS) + CHECK_ARGON2(LIBRECRYPT_ARGON2DS_V1_0); + CHECK_ARGON2(LIBRECRYPT_ARGON2DS_V1_3); +#else + CHECK_DISABLED(LIBRECRYPT_ARGON2DS_V1_0); + CHECK_DISABLED(LIBRECRYPT_ARGON2DS_V1_3); +#endif + + CHECK_DISABLED(LIBRECRYPT_HASH_ALGORITHM_END); + + /* Further tested in librecrypt_set_pepper.c */ + + librecrypt_free_context(ctx); + + STOP_RESOURCE_TEST(); + return 0; +} + + +#endif diff --git a/librecrypt_get_user_data.3 b/librecrypt_get_user_data.3 new file mode 100644 index 0000000..63187ee --- /dev/null +++ b/librecrypt_get_user_data.3 @@ -0,0 +1,49 @@ +.TH LIBRECRYPT_GET_USER_DATA 3 LIBRECRYPT +.SH NAME +librecrypt_get_user_data - Retrieve application-defined data + +.SH SYNOPSIS +.nf +#include + +void \fBlibrecrypt_get_user_data\fP(const LIBRECRYPT_CONTEXT *\fIctx\fP, void *\fIuser\fP); +.fi +.PP +Link with +.IR -lrecrypt . + +.SH DESCRIPTION +The +.BR librecrypt_get_user_data () +function returns a pointer to application-defined +data, stored using the +.BR librecrypt_set_user_data (3) +function, inside the +.IR ctx . + +.SH RETURN VALUES +The +.BR librecrypt_get_user_data () +function returns a pointer stored in +.I ctx +by the application, or +.I NULL +if no pointer or the +.I NULL +pointer was stored. + +.SH ERRORS +The +.BR librecrypt_get_user_data () +function cannot fail. + +.SH HISTORY +The +.BR librecrypt_get_user_data () +function was introduced in version 1.1 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_set_user_data (3), +.BR librecrypt_set_custom_algorithms (3) diff --git a/librecrypt_get_user_data.c b/librecrypt_get_user_data.c new file mode 100644 index 0000000..978794b --- /dev/null +++ b/librecrypt_get_user_data.c @@ -0,0 +1,24 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +void * +librecrypt_get_user_data(const LIBRECRYPT_CONTEXT *ctx) +{ + return ctx->user_data; +} + + +#else + + +CONST int +main(void) +{ + /* Tested in librecrypt_set_user_data.c */ + return 0; +} + + +#endif diff --git a/librecrypt_hash.3 b/librecrypt_hash.3 index 5be93c7..1cdea90 100644 --- a/librecrypt_hash.3 +++ b/librecrypt_hash.3 @@ -7,8 +7,8 @@ librecrypt_hash - Compute password hash encoded in ASCII without settings prefix #include ssize_t \fBlibrecrypt_hash\fP(char *restrict \fIout_buffer\fP, size_t \fIsize\fP, - const char *\fIphrase\fP, size_t \fIlen\fP, - const char *\fIsettings\fP, LIBRECRYPT_CONTEXT *\fIctx\fP); + const char *\fIphrase\fP, size_t \fIlen\fP, + const char *\fIsettings\fP, LIBRECRYPT_CONTEXT *\fIctx\fP); .fi .PP Link with diff --git a/librecrypt_hash_.c b/librecrypt_hash_.c index 8b1ab1d..89c8e88 100644 --- a/librecrypt_hash_.c +++ b/librecrypt_hash_.c @@ -119,12 +119,12 @@ next: prefix = 1u; /* $covered$ (TODO we currently don't have an algorithm to trigger this) */ } if (!algo->flexible_hash_size && prefix != n) - goto einval; /* $covered$ (TODO we currently don't have an algorithm to trigger this) */ + goto einval; /* TODO test with custom hash function */ /* Get hash size */ if (!algo->flexible_hash_size) { /* fixed */ - hash_size = algo->hash_size; /* $covered$ (TODO we currently don't have an algorithm to trigger this) */ + hash_size = algo->hash_size; /* TODO test with custom hash function */ } else if (prefix == n) { /* default */ hash_size = algo->hash_size; @@ -152,7 +152,7 @@ next: break; hash_size = i - prefix; if (algo->pad && algo->strict_pad) { - /* $covered{$ (TODO we currently don't have an algorithm to trigger this) */ + /* TODO test with custom hash function */ for (; i < n; i++) if (settings[i] != algo->pad) break; @@ -160,7 +160,6 @@ next: goto einval; if (i - prefix - hash_size >= 4u) goto einval; - /* $covered}$ */ } if (i != n) goto einval; @@ -242,7 +241,7 @@ next: ascii_len = hash_size % 3u; if (ascii_len) { if (algo->pad && algo->strict_pad) - ascii_len = 4u; /* padding to for bytes */ /* $covered$ (TODO we currently don't have an algorithm to trigger this) */ + ascii_len = 4u; /* padding to for bytes */ /* TODO test with custom hash function */ else ascii_len += 1u; /* 3n+m bytes: 4n+m+1 chars, unless m=0 */ } diff --git a/librecrypt_hash_algorithm_end.3 b/librecrypt_hash_algorithm_end.3 new file mode 100644 index 0000000..1c332ed --- /dev/null +++ b/librecrypt_hash_algorithm_end.3 @@ -0,0 +1,44 @@ +.TH LIBRECRYPT_HASH_ALGORITHM_END 3 LIBRECRYPT +.SH NAME +librecrypt_hash_algorithm_end - Number of hash algorithms the linked library knows about + +.SH SYNOPSIS +.nf +#include + +enum librecrypt_hash_algorithm { + LIBRECRYPT_ARGON2I_V1_0, + LIBRECRYPT_ARGON2I_V1_3, + LIBRECRYPT_ARGON2D_V1_0, + LIBRECRYPT_ARGON2D_V1_3, + LIBRECRYPT_ARGON2ID_V1_0, + LIBRECRYPT_ARGON2ID_V1_3, + LIBRECRYPT_ARGON2DS_V1_0, + LIBRECRYPT_ARGON2DS_V1_3, + LIBRECRYPT_HASH_ALGORITHM_END /* end of enum marker (always last) */ +}; + +extern const enum librecrypt_hash_algorithm \fBlibrecrypt_hash_algorithm_end\fP; +.fi +.PP +Link with +.IR -lrecrypt . + +.SH DESCRIPTION +The +.BR librecrypt_hash_algorithm_end +variable is set to the value +.I LIBRECRYPT_HASH_ALGORITHM_END +had when the library was compiled, meaning that +it is the linked library's version of +.I LIBRECRYPT_HASH_ALGORITHM_END +rather than the application's version. + +.SH HISTORY +The +.BR librecrypt_hash_algorithm_end +variable was introduced in version 1.1 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7) diff --git a/librecrypt_hash_algorithm_end.c b/librecrypt_hash_algorithm_end.c index adb1555..a4d1ef8 100644 --- a/librecrypt_hash_algorithm_end.c +++ b/librecrypt_hash_algorithm_end.c @@ -3,7 +3,7 @@ #ifndef TEST -enum librecrypt_hash_algorithm librecrypt_hash_algorithm_end = LIBRECRYPT_HASH_ALGORITHM_END; +const enum librecrypt_hash_algorithm librecrypt_hash_algorithm_end = LIBRECRYPT_HASH_ALGORITHM_END; #else diff --git a/librecrypt_hash_binary.3 b/librecrypt_hash_binary.3 index 9956f83..78568aa 100644 --- a/librecrypt_hash_binary.3 +++ b/librecrypt_hash_binary.3 @@ -7,8 +7,8 @@ librecrypt_hash_binary - Compute password hash in raw binary form #include ssize_t \fBlibrecrypt_hash_binary\fP(void *restrict \fIout_buffer\fP, size_t \fIsize\fP, - const char *\fIphrase\fP, size_t \fIlen\fP, - const char *\fIsettings\fP, LIBRECRYPT_CONTEXT *\fIctx\fP); + const char *\fIphrase\fP, size_t \fIlen\fP, + const char *\fIsettings\fP, LIBRECRYPT_CONTEXT *\fIctx\fP); .fi .PP Link with diff --git a/librecrypt_is_enabled.3 b/librecrypt_is_enabled.3 index 2827180..9ec13a4 100644 --- a/librecrypt_is_enabled.3 +++ b/librecrypt_is_enabled.3 @@ -7,15 +7,7 @@ librecrypt_is_enabled - Check whether an algorithm was enabled at compile-time #include enum librecrypt_hash_algorithm { - LIBRECRYPT_ARGON2I_V1_0, - LIBRECRYPT_ARGON2I_V1_3, - LIBRECRYPT_ARGON2D_V1_0, - LIBRECRYPT_ARGON2D_V1_3, - LIBRECRYPT_ARGON2ID_V1_0, - LIBRECRYPT_ARGON2ID_V1_3, - LIBRECRYPT_ARGON2DS_V1_0, - LIBRECRYPT_ARGON2DS_V1_3, - LIBRECRYPT_HASH_ALGORITHM_END /* end of enum marker (always last) */ + /* See librecrypt_set_pepper(3) */ }; int \fBlibrecrypt_is_enabled\fP(enum librecrypt_hash_algorithm \fIalgo\fP); @@ -29,10 +21,17 @@ The .BR librecrypt_is_enabled () function checks whether the the .B librecrypt -library end it's dependencies where compiled to +library and its dependencies where compiled to support the hash algorithm specified in the .I algo -parameter. +parameter. See the +.B EXTENDED DESCRIPTION +in +.BR librecrypt_set_pepper (3) +for listing of constants for +.B enum librecrypt_hash_algorithm +(and +.IR algo ). .SH RETURN VALUES The @@ -63,46 +62,6 @@ T} Async-signal safety AS-Safe .TE .sp -.SH EXTENDED DESCRIPTION -The following values for -.I algo -are supported and may cause the -.BR librecrypt_is_enabled () -function to return either 0 or 1 -(all other cause the function to return 0): -.TP -.B LIBRECRYPT_ARGON2I_V1_0 -Argon2i, version 1.0, which is presented by -\(dq$argon2i$v=13$\(dq, optionally without \(dq$v=13\(dq. -.TP -.B LIBRECRYPT_ARGON2I_V1_3 -Argon2i, version 1.3, which is presented by -\(dq$argon2i$v=19$\(dq. -.TP -.B LIBRECRYPT_ARGON2D_V1_0 -Argon2d, version 1.0, which is presented by -\(dq$argon2d$v=13$\(dq, optionally without \(dq$v=13\(dq. -.TP -.B LIBRECRYPT_ARGON2D_V1_3 -Argon2d, version 1.3, which is presented by -\(dq$argon2d$v=19$\(dq. -.TP -.B LIBRECRYPT_ARGON2ID_V1_0 -Argon2id, version 1.0, which is presented by -\(dq$argon2id$v=13$\(dq, optionally without \(dq$v=13\(dq. -.TP -.B LIBRECRYPT_ARGON2ID_V1_3 -Argon2id, version 1.3, which is presented by -\(dq$argon2id$v=19$\(dq. -.TP -.B LIBRECRYPT_ARGON2DS_V1_0 -Argon2ds, version 1.0, which is presented by -\(dq$argon2ds$v=13$\(dq, optionally without \(dq$v=13\(dq. -.TP -.B LIBRECRYPT_ARGON2DS_V1_3 -Argon2ds, version 1.3, which is presented by -\(dq$argon2ds$v=19$\(dq. - .SH HISTORY The .BR librecrypt_is_enabled () diff --git a/librecrypt_make_settings.3 b/librecrypt_make_settings.3 index 9b39bd9..45ac3c8 100644 --- a/librecrypt_make_settings.3 +++ b/librecrypt_make_settings.3 @@ -7,9 +7,9 @@ librecrypt_make_settings - Generate a password hash settings string #include ssize_t \fBlibrecrypt_make_settings\fP(char *\fIout_buffer\fP, size_t \fIsize\fP, const char *\fIalgorithm\fP, - size_t \fImemcost\fP, uintmax_t \fItimecost\fP, int \fIgensalt\fP, - ssize_t (*\fIrng\fP)(void *\fIout\fP, size_t \fIn\fP, void *\fIuser\fP), - void *\fIuser\fP, LIBRECRYPT_CONTEXT *\fIctx\fP); + size_t \fImemcost\fP, uintmax_t \fItimecost\fP, int \fIgensalt\fP, + ssize_t (*\fIrng\fP)(void *\fIout\fP, size_t \fIn\fP, void *\fIuser\fP), + void *\fIuser\fP, LIBRECRYPT_CONTEXT *\fIctx\fP); .fi .PP Link with diff --git a/librecrypt_realise_salts.3 b/librecrypt_realise_salts.3 index dba21b1..caeb908 100644 --- a/librecrypt_realise_salts.3 +++ b/librecrypt_realise_salts.3 @@ -7,8 +7,8 @@ librecrypt_realise_salts - Realise asterisk-encoded random salts in a settings s #include ssize_t \fBlibrecrypt_realise_salts\fP(char *restrict \fIout_buffer\fP, size_t \fIsize\fP, const char *\fIsettings\fP, - ssize_t (*\fIrng\fP)(void *\fIout\fP, size_t \fIn\fP, void *\fIuser\fP), void *\fIuser\fP, - LIBRECRYPT_CONTEXT *\fIctx\fP); + ssize_t (*\fIrng\fP)(void *\fIout\fP, size_t \fIn\fP, void *\fIuser\fP), void *\fIuser\fP, + LIBRECRYPT_CONTEXT *\fIctx\fP); .fi .PP Link with diff --git a/librecrypt_realise_salts.c b/librecrypt_realise_salts.c index 64d65c4..b2580eb 100644 --- a/librecrypt_realise_salts.c +++ b/librecrypt_realise_salts.c @@ -128,7 +128,7 @@ librecrypt_realise_salts(char *restrict out_buffer, size_t size, const char *set /* Write padding charaters */ right = MIN(right, size); for (i = 0u; i < right; i++) - out_buffer[right] = pad; /* $covered$ (TODO we currently don't have an algorithm to trigger this) */ + out_buffer[right] = pad; /* TODO test with custom hash function */ out_buffer = &out_buffer[right]; size -= right; } diff --git a/librecrypt_scan_settings.3 b/librecrypt_scan_settings.3 new file mode 100644 index 0000000..63825a8 --- /dev/null +++ b/librecrypt_scan_settings.3 @@ -0,0 +1,295 @@ +.TH LIBRECRYPT_SCAN_SETTINGS 3 LIBRECRYPT +.SH NAME +librecrypt_scan_settings - Parse and validate a password hash string + +.SH SYNOPSIS +.nf +#include + +int \fBlibrecrypt_scan_settings\fP(const char *\fIsettings\fP, size_t \fIlen\fP, const char *\fIfmt\fP, ...); +.fi +.PP +Link with +.IR -lrecrypt . + +.SH DESCRIPTION +The +.BR librecrypt_scan_settings () +function parses the +.I len +first bytes of +.I settings +according the a format specified in the +.I fmt +argument and checks that the entire +.I settings +string matches entirety of +.IR fmt . +.PP +The arguments after +.I fmt +specify output arguments and parameters +the format. The are placed in the same order +as the formatting codes in +.I fmt +that they are associated with. +.PP +.I fmt +must not be +.IR NULL . +.PP +Format codes in +.I fmt +are parsed left to right, anything that is +not a format code is a literal characters +that shall be matched exactly in +.IR settings . +The dollowing format codes are recognised: +.TP +.B %% +Shall match a literal procent sign +.RB ( % ). +No argument is placed after +.IR fmt . +.TP +.B %* +Greedily matches any sequence of non-dollar-sign +bytes +.RB (non- $ ). +No argument is placed after +.IR fmt . +.TP +.B %s +Match a string. The possible strings to match +with are placed after +.IR fmt . +An additional +.I NULL +is placed after the last string to terminate the +list of matchable strings. The first string +that matches is selected, meaning that the longest +string shall be specified first if there are two +that are identical except that one is truncated +(as fewer characters to the right). The strings +shall have the type +.BR "const char *" . +.TP +.B %^s +Identical to +.BR %s , +except an additional argument is place before the +first string. This argument shall have the type +.BR "const char **" . +The reference to matched string will be stored +at the memory the pointer points to. +.TP +.B %u +.TQ +.B %p +Match an unsigned, decimal integer. For +.BR %u , +but not for +.BR %p , +leading zeroes are allowed. The minimum value +that may be matched shall be placed after +.I fmt +as a +.BR uintmax_t , +followed by the maximum value +(also a +.BR uintmax_t ). +.TP +.B %^u +.TQ +.B %^p +Match an unsigned, decimal integer. For +.BR %u , +but not for +.BR %p , +leading zeroes are allowed. An output +argument, with the type +.B uintmax_t * +shall be placed after +.IR fmt , +followed by the minimum value as a +.B uintmax_t +and then the maximum value +(also a +.BR uintmax_t ). +.TP +.B %b +.TQ +.B %h +.TQ +.B %^b +.TQ +.B %^h +.TQ +.B %&b +.TQ +.B %&h +Match base64-encoded data or asterisk-encoded +data size. The arguments after +.I fmt +shall be, in order: +.RS +.TP +* +(Only for +.B %&b +and +.BR %&h .) +.br +An output argument with the type +.B const char ** +that will be set to point to the +beginning, in +.IR settings , +of the base64-encoded data. If +asterisk-encodng is used, +.I NULL +will be stored. +.TP +* +(Only for +.BR %^b , +.BR %^h , +.BR %&b , +and +.BR %&h .) +.br +An output argument with the type +.BR "uintmax_t *" . +For asterisk-encoding, it will be +set to the encoded value. +For base64-encoding, it will be set +to the encoded number of bytes for +.B %^b +and +.BR %^h , +but to the number of base-64 letters +used to encode the data for +.B %&b +and +.BR %&h . +.TP +* +The minimum number of bytes in the +binary data, as a +.BR uintmax_t . + +For +.BR %h , +.BR %^h , +and +.BR %&h , +the empty data is allowed even if the +bounary is set to a positive value. This +is the differenc between +.BR %h , +.BR %^h , +and +.B %&h +and +.BR %b , +.BR %^b , +and +.BR %&b . +.TP +* +The maximum number of bytes in the +binary data, as a +.BR uintmax_t . +.TP +* +The base64 byte-to-ASCII decoding +table, as a +.BR "const unsigned char[static 256]" . +.TP +* +The base64 padding character, or the null +byte if there isn't one. The type shall be +.BR char . +.TP +* +1 if the base64 encoding is always minimally +padded at a multiple of 4 base64 letters +(when it isn't already), and 0 otherwise. +The type shall be +.BR int . +.RE + +.SH RETURN VALUES +The +.BR librecrypt_scan_settings () +function returns 1 if the +.I settings +match +.IR fmt , +and 0 otherwise. + +.SH ERRORS +The +.BR librecrypt_scan_settings () +function cannot fail, however if +.I fmt +is malformatted, it will call +.BR abort (3) +to terminate the process. + +.SH ATTRIBUTES +For an explanation of the terms used in this section, see +.BR attributes (7). +.PP +.TS +allbox; +lb lb lb +l l l. +Interface Attribute Value +T{ +.BR librecrypt_scan_settings () +T} Thread safety MT-Safe +T{ +.BR librecrypt_scan_settings () +T} Async-signal safety AS-Safe +.TE +.sp + +.SH HISTORY +The +.BR librecrypt_scan_settings () +function was introduced in version 1.1 of +.BR librecrypt . + +.SH NOTES +Parsing of +.I settings +does not stop at it's first null byte, +and it is not expected to have one, instead +it strictly stops once +.I len +bytes have been read, or when the string +stops matching +.I fmt +(whichever comes earlier). +.I fmt +however is expected to be null-byte +terminated, and if the null byte has been +reached before all +.I len +bytes of +.I settings +have been read, +.I settings +does not match +.IR fmt . +.PP +The +.BR librecrypt_scan_settings (3) +function is provided as a helper function +for those wishing to implement custom hash +algorithms. + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_set_custom_algorithms (3) diff --git a/librecrypt_scan_settings.c b/librecrypt_scan_settings.c new file mode 100644 index 0000000..749797e --- /dev/null +++ b/librecrypt_scan_settings.c @@ -0,0 +1,799 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +/** + * Parse and validate an unsigned, decimal integer + * + * @param settings The password hash string + * @param off The current in `settings` where the integer + * begins; will be updated to its ends (one byte + * after the last character encoding the integer) + * @param len The number of bytes in `settings` + * @param min_first_digit '0' if leading zeroes are permitted, '1' otherwise + * @param min The smallest legal value + * @param max The greatest legal value + * @param out Output parameter for the encoded integer, or `NULL` + * @return 1 if a valid integer was encoded, 0 otherwise + */ +static int +check_uint(const char *settings, size_t *off, size_t len, char min_first_digit, + uintmax_t min, uintmax_t max, uintmax_t *out) +{ + uintmax_t digit, value; + + /* Reject the empty string */ + if (*off >= len) + return 0; + + /* Reject leading zeroes unless `min_first_digit` is '0' */ + if (min_first_digit > settings[*off] || settings[*off] > '9') { + /* With leading zeroes being rejected, a single '0' may be + * parsed as the value 0 (if that value is permitted) */ + if (min == 0u && min_first_digit != '0' && settings[*off] == '0') { + ++*off; + if (out) + *out = 0u; + return 1; + } + return 0; + } + + /* Decode the integer */ + value = (uintmax_t)(settings[*off] - '0'); + ++*off; + while (*off < len && '0' <= settings[*off] && settings[*off] <= '9') { + digit = (uintmax_t)(settings[*off] - '0'); + if (value > (UINTMAX_MAX - digit) / 10u) + return 0; + value *= 10u; + value += digit; + ++*off; + } + if (out) + *out = value; + + /* Check that the integer is within the accepted range */ + return min <= value && value <= max; +} + + +/** + * Validate and returns a salt or hash that's either encoded + * in base-64 or has its length encoded using asterisk-notation + * + * This function does not check the value of any excess bit + * in the base-64 encoding + * + * @param settings The password hash string + * @param off The current in `settings` where the integer + * begins; will be updated to its ends (one byte + * after the last character encoding the integer) + * @param len The number of bytes in `settings` + * @param min The least allowed number of bytes + * @param max The most allowed number of bytes + * @param allow_empty Whether the empty string is allowed + * (no encoded bytes and no asterisk-notation) + * @param dlut Alphabet reverse lookup table, shall map any valid + * character (except the padding character) to the value + * of that character in the encoding alphabet, and map + * any other character to the value `0xFF` + * @param pad The padding character to used at the end; the NUL byte if none + * @param strict_pad Zero if the padding at the end is optional, non-zero otherwise + * @param strout Output parameter for the beginning of the base-64 text, + * set to `NULL` if asterisk-notation is used + * @param lenout Output parameter for the number of bytes in `*strout`, or if + * `*strout` is set to `NULL`, the asterisk-encoded number; + * however if `strout` is `NULL`, the number bytes used by + * the salt or hash (when in raw binary format) is stored + * @return 1 if the encoded value was of proper length, + * a proper length was encoded using asterisk-notation, or + * if `allow_empty` was non-zero, nothing was encoded; + * 0 otherwise + */ +static int +check_data(const char *settings, size_t *off, size_t len, uintmax_t min, uintmax_t max, int allow_empty, + const unsigned char dlut[restrict static 256], char pad, int strict_pad, + const char **strout, uintmax_t *lenout) +{ + size_t i, old_i; + uintmax_t q, r, n; + + /* Check for asterisk-notation */ + if (*off < len && settings[*off] == '*') { + ++*off; + if (strout) + *strout = NULL; + return check_uint(settings, off, len, '0', min, max, lenout); + } + + settings = &settings[*off]; + len -= *off; + i = 0u; + + /* Get number of base-64 characters available, excluding padding */ + while (i < len && dlut[(unsigned char)settings[i]] < 64u) + i++; + + /* Calculate number of encoded bytes */ + q = i / 4u; + r = i % 4u; + n = q * 3u + r - (r ? 1u : 0u); + if (!strout && lenout) + *lenout = n; + /* 1 base-64 character in excees of a multiple of 4, + * this is illegal because 4 characters encode 3 bytes + * without any excees bits (both are 24 bits), so + * if the number of encoded bytes is not a multiple + * of 3, at least 2 characters in excees of multiple + * of 4 is required because at least 2 characeters is + * required to encode 1 byte in base-64 */ + if (r == 1u) + return 0; + + /* Check number of bytes is within the specified range, + * but accept 0 if `allow_empty` is non-zero */ + if (min > n || n > max) + if (n || !allow_empty) + return 0; + + /* Check padding if allowed or even required */ + if (r && pad) { + old_i = i; + while (r < 4u && i < len && settings[i] == pad) { + i++; + r++; + } + if ((i != old_i || strict_pad) && r != 4u) + return 0; + } + + /* Return the base-64 encoding */ + if (strout) { + *strout = settings; + *lenout = (uintmax_t)i; + } + + *off += i; + return 1; +} + + +int +librecrypt_scan_settings(const char *settings, size_t len, const char *fmt, ...) +{ + size_t i = 0u; + uintmax_t *uout, umin, umax; + const unsigned char *dlut; + char pad; + int strict_pad; + int output = 0; + va_list args; + const char *str, **strout; + size_t n; + + va_start(args, fmt); + + while (*fmt) { + if (*fmt != '%') { + /* Normal literal character */ + if (i == len) + return 0; + if (settings[i++] != *fmt++) + return 0; + + } else if (fmt[1u] == '%') { + /* '%'-escaped literal '%' ("%%") */ + if (i == len) + return 0; + if (settings[i++] != '%') + return 0; + fmt = &fmt[2u]; + + } else if (fmt[1u] == '*') { + /* "%*": skip to next '$' or the end of no '$' was found */ + while (i < len && settings[i] != '$') + i++; + fmt = &fmt[2u]; + + } else if (fmt[1u] == '^') { + /* '^' inserted between '%' and some letter may be used + * have the value returned */ + output = 1; + fmt++; + goto outable; + + } else if (fmt[1u] == '&' && (fmt[2u] == 'b' || fmt[2u] == 'h')) { + /* Like "%b"/"%h" except output pointer to base-64 text or decodes asterisk-notation */ + strout = va_arg(args, const char **); + uout = va_arg(args, uintmax_t *); + fmt++; + goto plain_bh; + + } else outable: if (fmt[1u] == 'p' || fmt[1u] == 'u') { + /* Unsigned integers ("%p" for leading zeros forbidden, "%u" for leading zeros allowed) */ + uout = output ? va_arg(args, uintmax_t *) : NULL; + umin = va_arg(args, uintmax_t); + umax = va_arg(args, uintmax_t); + if (!check_uint(settings, &i, len, fmt[1u] == 'p' ? '1' : '0', umin, umax, uout)) + return 0; + goto outable_done; + + } else if (fmt[1u] == 'b' || fmt[1u] == 'h') { + /* Base-64 or asterisk-notation ("%b" for normal, "%h" for "" allowed) */ + strout = NULL; + uout = output ? va_arg(args, uintmax_t *) : NULL; + plain_bh: + umin = va_arg(args, uintmax_t); + umax = va_arg(args, uintmax_t); + dlut = va_arg(args, const unsigned char *); + pad = (char)va_arg(args, int); /* `char` is promoted to `int` when passed through `...` */ + strict_pad = va_arg(args, int); + if (!check_data(settings, &i, len, umin, umax, fmt[1u] == 'h', dlut, pad, strict_pad, strout, uout)) + return 0; + goto outable_done; + + } else if (fmt[1u] == 's') { + /* String from fixed set ("%s") */ + strout = output ? va_arg(args, const char **) : NULL; + for (;;) { + str = va_arg(args, const char *); + if (!str) + return 0; + n = strlen(str); + if (n > len - i) + continue; + if (strncmp(&settings[i], str, n)) + continue; + if (strout) + *strout = str; + i += n; + break; + } + while ((str = va_arg(args, const char *))); + goto outable_done; + + } else { + abort(); /* $covered$ */ + + outable_done: + output = 0; + fmt = &fmt[2u]; + } + } + + va_end(args); + + return i == len; +} + + + +#else + + +#define RANGE(A, B) (uintmax_t)(A), (uintmax_t)(B) +#define BASE64(A, B) RANGE(A, B), lut, pad, strict_pad + + +static unsigned char lut[256]; + + +static void +check_asterisk(char pad, int strict_pad) +{ + uintmax_t u, u2; + const char *s, *s2; + + EXPECT(librecrypt_scan_settings("*012", 4u, "%b", BASE64(0, 100)) == 1); + EXPECT(librecrypt_scan_settings("*200", 4u, "%b", BASE64(0, 100)) == 0); + EXPECT(librecrypt_scan_settings("*012", 4u, "%b", BASE64(100, 200)) == 0); + EXPECT(librecrypt_scan_settings("*0", 2u, "%b", BASE64(0, 100)) == 1); + EXPECT(librecrypt_scan_settings("*00", 3u, "%b", BASE64(0, 100)) == 1); + EXPECT(librecrypt_scan_settings("", 0u, "%b", BASE64(0, 100)) == 1); + EXPECT(librecrypt_scan_settings("", 0u, "%b", BASE64(5, 100)) == 0); + + EXPECT(librecrypt_scan_settings("*012.", 5u, "%b.", BASE64(0, 100)) == 1); + EXPECT(librecrypt_scan_settings("*012-", 5u, "%b.", BASE64(0, 100)) == 0); + EXPECT(librecrypt_scan_settings("*012", 4u, "%b.", BASE64(0, 100)) == 0); + + EXPECT(librecrypt_scan_settings("*2*5", 4u, "%b%b", BASE64(2, 2), BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("*2*4", 4u, "%b%b", BASE64(2, 2), BASE64(5, 5)) == 0); + + EXPECT(librecrypt_scan_settings("*012", 4u, "%h", BASE64(0, 100)) == 1); + EXPECT(librecrypt_scan_settings("*200", 4u, "%h", BASE64(0, 100)) == 0); + EXPECT(librecrypt_scan_settings("*012", 4u, "%h", BASE64(100, 200)) == 0); + EXPECT(librecrypt_scan_settings("*0", 2u, "%h", BASE64(0, 100)) == 1); + EXPECT(librecrypt_scan_settings("*00", 3u, "%h", BASE64(0, 100)) == 1); + EXPECT(librecrypt_scan_settings("", 0u, "%h", BASE64(0, 100)) == 1); + EXPECT(librecrypt_scan_settings("", 0u, "%h", BASE64(5, 100)) == 1); + + EXPECT(librecrypt_scan_settings("*012.", 5u, "%h.", BASE64(0, 100)) == 1); + EXPECT(librecrypt_scan_settings("*012-", 5u, "%h.", BASE64(0, 100)) == 0); + EXPECT(librecrypt_scan_settings("*012", 4u, "%h.", BASE64(0, 100)) == 0); + + EXPECT(librecrypt_scan_settings("*2*5", 4u, "%h%h", BASE64(2, 2), BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("*2*4", 4u, "%h%h", BASE64(2, 2), BASE64(5, 5)) == 0); + + u = 0u; + EXPECT(librecrypt_scan_settings("*012", 4u, "%^b", &u, BASE64(0, 100)) == 1); + EXPECT(u == 12u); + EXPECT(librecrypt_scan_settings("*200", 4u, "%^b", &u, BASE64(0, 100)) == 0); + EXPECT(librecrypt_scan_settings("*012", 4u, "%^b", &u, BASE64(100, 200)) == 0); + u = 99u; + EXPECT(librecrypt_scan_settings("*0", 2u, "%^b", &u, BASE64(0, 100)) == 1); + EXPECT(u == 0u); + u = 99u; + EXPECT(librecrypt_scan_settings("*00", 3u, "%^b", &u, BASE64(0, 100)) == 1); + EXPECT(u == 0u); + u = 99u; + EXPECT(librecrypt_scan_settings("", 0u, "%^b", &u, BASE64(0, 100)) == 1); + EXPECT(u == 0u); + EXPECT(librecrypt_scan_settings("", 0u, "%^b", &u, BASE64(5, 100)) == 0); + + EXPECT(librecrypt_scan_settings("*012.", 5u, "%^b.", &u, BASE64(0, 100)) == 1); + EXPECT(librecrypt_scan_settings("*012-", 5u, "%^b.", &u, BASE64(0, 100)) == 0); + EXPECT(librecrypt_scan_settings("*012", 4u, "%^b.", &u, BASE64(0, 100)) == 0); + + u = u2 = 99u; + EXPECT(librecrypt_scan_settings("*2*5", 4u, "%^b%^b", &u, BASE64(2, 2), &u2, BASE64(5, 5)) == 1); + EXPECT(u == 2u); + EXPECT(u2 == 5u); + EXPECT(librecrypt_scan_settings("*2*4", 4u, "%^b%^b", &u, BASE64(2, 2), &u2, BASE64(5, 5)) == 0); + + u = 0u; + EXPECT(librecrypt_scan_settings("*012", 4u, "%^h", &u, BASE64(0, 100)) == 1); + EXPECT(u == 12u); + EXPECT(librecrypt_scan_settings("*200", 4u, "%^h", &u, BASE64(0, 100)) == 0); + EXPECT(librecrypt_scan_settings("*012", 4u, "%^h", &u, BASE64(100, 200)) == 0); + u = 99u; + EXPECT(librecrypt_scan_settings("*0", 2u, "%^h", &u, BASE64(0, 100)) == 1); + EXPECT(u == 0u); + u = 99u; + EXPECT(librecrypt_scan_settings("*00", 3u, "%^h", &u, BASE64(0, 100)) == 1); + EXPECT(u == 0u); + u = 99u; + EXPECT(librecrypt_scan_settings("", 0u, "%^h", &u, BASE64(0, 100)) == 1); + EXPECT(u == 0u); + u = 99u; + EXPECT(librecrypt_scan_settings("", 0u, "%^h", &u, BASE64(5, 100)) == 1); + EXPECT(u == 0u); + + EXPECT(librecrypt_scan_settings("*012.", 5u, "%^h.", &u, BASE64(0, 100)) == 1); + EXPECT(librecrypt_scan_settings("*012-", 5u, "%^h.", &u, BASE64(0, 100)) == 0); + EXPECT(librecrypt_scan_settings("*012", 4u, "%^h.", &u, BASE64(0, 100)) == 0); + + u = u2 = 99u; + EXPECT(librecrypt_scan_settings("*2*5", 4u, "%^h%^h", &u, BASE64(2, 2), &u2, BASE64(5, 5)) == 1); + EXPECT(u == 2u); + EXPECT(u2 == 5u); + EXPECT(librecrypt_scan_settings("*2*4", 4u, "%^h%^h", &u, BASE64(2, 2), &u2, BASE64(5, 5)) == 0); + + s = ""; + u = 99u; + EXPECT(librecrypt_scan_settings("*5.", 3, "%&b.", &s, &u, BASE64(2, 100)) == 1); + EXPECT(s == NULL); + EXPECT(u == 5u); + + s = ""; + u = 99u; + EXPECT(librecrypt_scan_settings("*5", 2, "%&b", &s, &u, BASE64(2, 100)) == 1); + EXPECT(s == NULL); + EXPECT(u == 5u); + + EXPECT(librecrypt_scan_settings("*5", 2, "%&b", &s, &u, BASE64(1, 4)) == 0); + EXPECT(librecrypt_scan_settings("*5", 2, "%&b", &s, &u, BASE64(6, 9)) == 0); + + s = s2 = ""; + u = u2 = 99u; + EXPECT(librecrypt_scan_settings("*5*10", 5, "%&b%&b", &s, &u, BASE64(2, 100), &s2, &u2, BASE64(2, 100)) == 1); + EXPECT(s == NULL); + EXPECT(u == 5u); + EXPECT(s2 == NULL); + EXPECT(u2 == 10u); + + EXPECT(librecrypt_scan_settings("*", 1u, "%b", BASE64(0, 10)) == 0); + EXPECT(librecrypt_scan_settings("*", 1u, "%h", BASE64(0, 10)) == 0); + EXPECT(librecrypt_scan_settings("*", 1u, "%^b", &u, BASE64(0, 10)) == 0); + EXPECT(librecrypt_scan_settings("*", 1u, "%^h", &u, BASE64(0, 10)) == 0); + EXPECT(librecrypt_scan_settings("*", 1u, "%&b", &s, &u, BASE64(0, 10)) == 0); + EXPECT(librecrypt_scan_settings("*", 1u, "%&h", &s, &u, BASE64(0, 10)) == 0); + EXPECT(librecrypt_scan_settings("*x", 2u, "%b", BASE64(0, 10)) == 0); + EXPECT(librecrypt_scan_settings("*x", 2u, "%h", BASE64(0, 10)) == 0); + EXPECT(librecrypt_scan_settings("*x", 2u, "%^b", &u, BASE64(0, 10)) == 0); + EXPECT(librecrypt_scan_settings("*x", 2u, "%^h", &u, BASE64(0, 10)) == 0); + EXPECT(librecrypt_scan_settings("*x", 2u, "%&b", &s, &u, BASE64(0, 10)) == 0); + EXPECT(librecrypt_scan_settings("*x", 2u, "%&h", &s, &u, BASE64(0, 10)) == 0); +} + + +static void +check_base64(char pad, int strict_pad) +{ + uintmax_t u; + const char *s; + + EXPECT(librecrypt_scan_settings("", 0u, "%b", BASE64(0, 100)) == 1); + EXPECT(librecrypt_scan_settings("", 0u, "%b", BASE64(1, 100)) == 0); + EXPECT(librecrypt_scan_settings("abcd", 4u, "%b", BASE64(3, 3)) == 1); + EXPECT(librecrypt_scan_settings("abcd", 4u, "%b", BASE64(4, 4)) == 0); + + EXPECT(librecrypt_scan_settings("", 0u, "%h", BASE64(0, 100)) == 1); + EXPECT(librecrypt_scan_settings("", 0u, "%h", BASE64(1, 100)) == 1); + EXPECT(librecrypt_scan_settings("abcd", 4u, "%h", BASE64(3, 3)) == 1); + EXPECT(librecrypt_scan_settings("abcd", 4u, "%h", BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcd", 4u, "%h", BASE64(2, 2)) == 0); + + u = 99u; + EXPECT(librecrypt_scan_settings("", 0u, "%^b", &u, BASE64(0, 100)) == 1); + EXPECT(u == 0u); + EXPECT(librecrypt_scan_settings("", 0u, "%^b", &u, BASE64(1, 100)) == 0); + u = 99u; + EXPECT(librecrypt_scan_settings("abcd", 4u, "%^b", &u, BASE64(3, 3)) == 1); + EXPECT(u == 3u); + EXPECT(librecrypt_scan_settings("abcd", 4u, "%^b", &u, BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcd", 4u, "%^b", &u, BASE64(2, 2)) == 0); + + u = 99u; + EXPECT(librecrypt_scan_settings("", 0u, "%^h", &u, BASE64(0, 100)) == 1); + EXPECT(u == 0u); + u = 99u; + EXPECT(librecrypt_scan_settings("", 0u, "%^h", &u, BASE64(1, 100)) == 1); + EXPECT(u == 0u); + u = 99u; + EXPECT(librecrypt_scan_settings("abcd", 4u, "%^h", &u, BASE64(3, 3)) == 1); + EXPECT(u == 3u); + EXPECT(librecrypt_scan_settings("abcd", 4u, "%^h", &u, BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcd", 4u, "%^h", &u, BASE64(2, 2)) == 0); + + s = NULL; + u = 99u; + EXPECT(librecrypt_scan_settings("_", 1u, "_%&b", &s, &u, BASE64(0, 100)) == 1); + EXPECT(u == 0u); + EXPECT(s && !strcmp(s, "")); + EXPECT(librecrypt_scan_settings("_", 1u, "_%&b", &s, &u, BASE64(1, 100)) == 0); + s = NULL; + u = 99u; + EXPECT(librecrypt_scan_settings("_abcd", 5u, "_%&b", &s, &u, BASE64(3, 3)) == 1); + EXPECT(u == 4u); + EXPECT(s && !strcmp(s, "abcd")); + EXPECT(librecrypt_scan_settings("_abcd", 5u, "_%&b", &s, &u, BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("_abcd", 5u, "_%&b", &s, &u, BASE64(2, 2)) == 0); + + s = NULL; + u = 99u; + EXPECT(librecrypt_scan_settings("_", 1u, "_%&h", &s, &u, BASE64(0, 100)) == 1); + EXPECT(u == 0u); + EXPECT(s && !strcmp(s, "")); + s = NULL; + u = 99u; + EXPECT(librecrypt_scan_settings("_", 1u, "_%&h", &s, &u, BASE64(1, 100)) == 1); + EXPECT(u == 0u); + EXPECT(s && !strcmp(s, "")); + s = NULL; + u = 99u; + EXPECT(librecrypt_scan_settings("_abcd", 5u, "_%&h", &s, &u, BASE64(3, 3)) == 1); + EXPECT(u == 4u); + EXPECT(s && !strcmp(s, "abcd")); + EXPECT(librecrypt_scan_settings("_abcd", 5u, "_%&h", &s, &u, BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("_abcd", 5u, "_%&h", &s, &u, BASE64(2, 2)) == 0); + + u = 99u; + EXPECT(librecrypt_scan_settings("_abcd_", 6u, "_%&b_", &s, &u, BASE64(3, 3)) == 1); + EXPECT(u == 4u); + EXPECT(s && !strcmp(s, "abcd_")); + u = 99u; + EXPECT(librecrypt_scan_settings("_abcd_", 6u, "_%&h_", &s, &u, BASE64(3, 3)) == 1); + EXPECT(u == 4u); + EXPECT(s && !strcmp(s, "abcd_")); + + u = 99u; + EXPECT(librecrypt_scan_settings("abcdabcd", 8u, "%&b", &s, &u, BASE64(6, 6)) == 1); + EXPECT(u == 8u); + EXPECT(s && strlen(s) == u); + + u = 99u; + EXPECT(librecrypt_scan_settings("abcdabcd", 8u, "%&h", &s, &u, BASE64(6, 6)) == 1); + EXPECT(u == 8u); + EXPECT(s && strlen(s) == u); + + EXPECT(librecrypt_scan_settings("abcda", 5u, "%&b", &s, &u, BASE64(1, 10)) == 0); + EXPECT(librecrypt_scan_settings("abcda---", 8u, "%&b", &s, &u, BASE64(1, 10)) == 0); + EXPECT(librecrypt_scan_settings("abcda---", 8u, "%&b---", &s, &u, BASE64(1, 10)) == 0); + EXPECT(librecrypt_scan_settings("abcda", 5u, "%&h", &s, &u, BASE64(1, 10)) == 0); + EXPECT(librecrypt_scan_settings("abcda---", 8u, "%&h", &s, &u, BASE64(1, 10)) == 0); + EXPECT(librecrypt_scan_settings("abcda---", 8u, "%&h---", &s, &u, BASE64(1, 10)) == 0); + + if (pad && strict_pad) { + EXPECT(librecrypt_scan_settings("abcdab", 6u, "%&b", &s, &u, BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdab", 6u, "%&h", &s, &u, BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc", 7u, "%&b", &s, &u, BASE64(5, 5)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc", 7u, "%&h", &s, &u, BASE64(5, 5)) == 0); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%&b", &s, &u, BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%&h", &s, &u, BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%&b", &s, &u, BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%&h", &s, &u, BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%&b--", &s, &u, BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%&h--", &s, &u, BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%&b-", &s, &u, BASE64(5, 5)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%&h-", &s, &u, BASE64(5, 5)) == 0); + } else if (pad) { + EXPECT(librecrypt_scan_settings("abcdab", 6u, "%&b", &s, &u, BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdab", 6u, "%&h", &s, &u, BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc", 7u, "%&b", &s, &u, BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc", 7u, "%&h", &s, &u, BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%&b", &s, &u, BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%&h", &s, &u, BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%&b", &s, &u, BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%&h", &s, &u, BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%&b--", &s, &u, BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%&h--", &s, &u, BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%&b-", &s, &u, BASE64(5, 5)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%&h-", &s, &u, BASE64(5, 5)) == 0); + } else { + EXPECT(librecrypt_scan_settings("abcdab", 6u, "%&b", &s, &u, BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdab", 6u, "%&h", &s, &u, BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc", 7u, "%&b", &s, &u, BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc", 7u, "%&h", &s, &u, BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%&b", &s, &u, BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%&h", &s, &u, BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%&b", &s, &u, BASE64(5, 5)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%&h", &s, &u, BASE64(5, 5)) == 0); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%&b--", &s, &u, BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%&h--", &s, &u, BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%&b-", &s, &u, BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%&h-", &s, &u, BASE64(5, 5)) == 1); + } + + if (pad && strict_pad) { + EXPECT(librecrypt_scan_settings("abcdab", 6u, "%^b", &u, BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdab", 6u, "%^h", &u, BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc", 7u, "%^b", &u, BASE64(5, 5)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc", 7u, "%^h", &u, BASE64(5, 5)) == 0); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%^b", &u, BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%^h", &u, BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%^b", &u, BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%^h", &u, BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%^b--", &u, BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%^h--", &u, BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%^b-", &u, BASE64(5, 5)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%^h-", &u, BASE64(5, 5)) == 0); + } else if (pad) { + EXPECT(librecrypt_scan_settings("abcdab", 6u, "%^b", &u, BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdab", 6u, "%^h", &u, BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc", 7u, "%^b", &u, BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc", 7u, "%^h", &u, BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%^b", &u, BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%^h", &u, BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%^b", &u, BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%^h", &u, BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%^b--", &u, BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%^h--", &u, BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%^b-", &u, BASE64(5, 5)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%^h-", &u, BASE64(5, 5)) == 0); + } else { + EXPECT(librecrypt_scan_settings("abcdab", 6u, "%^b", &u, BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdab", 6u, "%^h", &u, BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc", 7u, "%^b", &u, BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc", 7u, "%^h", &u, BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%^b", &u, BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%^h", &u, BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%^b", &u, BASE64(5, 5)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%^h", &u, BASE64(5, 5)) == 0); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%^b--", &u, BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%^h--", &u, BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%^b-", &u, BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%^h-", &u, BASE64(5, 5)) == 1); + } + + if (pad && strict_pad) { + EXPECT(librecrypt_scan_settings("abcdab", 6u, "%b", BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdab", 6u, "%h", BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc", 7u, "%b", BASE64(5, 5)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc", 7u, "%h", BASE64(5, 5)) == 0); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%b", BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%h", BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%b", BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%h", BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%b--", BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%h--", BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%b-", BASE64(5, 5)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%h-", BASE64(5, 5)) == 0); + } else if (pad) { + EXPECT(librecrypt_scan_settings("abcdab", 6u, "%b", BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdab", 6u, "%h", BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc", 7u, "%b", BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc", 7u, "%h", BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%b", BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%h", BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%b", BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%h", BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%b--", BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%h--", BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%b-", BASE64(5, 5)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%h-", BASE64(5, 5)) == 0); + } else { + EXPECT(librecrypt_scan_settings("abcdab", 6u, "%b", BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdab", 6u, "%h", BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc", 7u, "%b", BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc", 7u, "%h", BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%b", BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%h", BASE64(4, 4)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%b", BASE64(5, 5)) == 0); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%h", BASE64(5, 5)) == 0); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%b--", BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdab--", 8u, "%h--", BASE64(4, 4)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%b-", BASE64(5, 5)) == 1); + EXPECT(librecrypt_scan_settings("abcdabc-", 8u, "%h-", BASE64(5, 5)) == 1); + } +} + + +static int discarded_int; + +int +main(void) +{ + const char *s, *s2; + uintmax_t u, u2; + + SET_UP_ALARM(); + INIT_TEST_ABORT(); + INIT_RESOURCE_TEST(); + + memset(lut, 255, sizeof(lut)); + lut['a'] = lut['b'] = lut['c'] = lut['d'] = 0; + + EXPECT(librecrypt_scan_settings("", 0u, "") == 1); + EXPECT(librecrypt_scan_settings("hej", 3u, "hej") == 1); + EXPECT(librecrypt_scan_settings("hej", 2u, "hej") == 0); + EXPECT(librecrypt_scan_settings("hej", 4u, "hej") == 0); + EXPECT(librecrypt_scan_settings("tja", 3u, "hej") == 0); + + EXPECT(librecrypt_scan_settings("%", 1u, "%%") == 1); + EXPECT(librecrypt_scan_settings("%", 0u, "%%") == 0); + EXPECT(librecrypt_scan_settings("x", 1u, "%%") == 0); + + EXPECT(librecrypt_scan_settings("hello", 5u, "%*") == 1); + EXPECT(librecrypt_scan_settings("hello$world", 11u, "%*$world") == 1); + EXPECT(librecrypt_scan_settings("hello$world", 11u, "%*$WORLD") == 0); + + EXPECT(librecrypt_scan_settings("hej.", 4u, "%s.", "hej", "tja", NULL) == 1); + EXPECT(librecrypt_scan_settings("tja.", 4u, "%s.", "hej", "tja", NULL) == 1); + EXPECT(librecrypt_scan_settings("bye.", 4u, "%s.", "hej", "tja", NULL) == 0); + EXPECT(librecrypt_scan_settings("hej-", 4u, "%s.", "hej", "tja", NULL) == 0); + EXPECT(librecrypt_scan_settings("tja-", 4u, "%s.", "hej", "tja", NULL) == 0); + EXPECT(librecrypt_scan_settings("bye-", 4u, "%s.", "hej", "tja", NULL) == 0); + EXPECT(librecrypt_scan_settings("hej.", 3u, "%s.", "hej", "tja", NULL) == 0); + EXPECT(librecrypt_scan_settings("tja.", 3u, "%s.", "hej", "tja", NULL) == 0); + EXPECT(librecrypt_scan_settings("bye.", 3u, "%s.", "hej", "tja", NULL) == 0); + EXPECT(librecrypt_scan_settings("hej.", 3u, "%s", "hej", "tja", NULL) == 1); + EXPECT(librecrypt_scan_settings("tja.", 3u, "%s", "hej", "tja", NULL) == 1); + EXPECT(librecrypt_scan_settings("bye.", 3u, "%s", "hej", "tja", NULL) == 0); + EXPECT(librecrypt_scan_settings("hej.", 4u, "%s", "hej", "tja", NULL) == 0); + EXPECT(librecrypt_scan_settings("tja.", 4u, "%s", "hej", "tja", NULL) == 0); + EXPECT(librecrypt_scan_settings("bye.", 4u, "%s", "hej", "tja", NULL) == 0); + EXPECT(librecrypt_scan_settings(".", 1u, "%s.", "hej", "tja", "", NULL) == 1); + EXPECT(librecrypt_scan_settings("tja.", 4u, "%s.", "hej", "tja", "", NULL) == 1); + EXPECT(librecrypt_scan_settings("tja.", 4u, "%s.", "", "hej", "tja", NULL) == 0); + EXPECT(librecrypt_scan_settings("tja.", 4u, "%s.", NULL) == 0); + + EXPECT(librecrypt_scan_settings("hejsan", 1u, "%s", "hej", NULL) == 0); + EXPECT(librecrypt_scan_settings("hejsan", 3u, "%s", "hej", NULL) == 1); + EXPECT(librecrypt_scan_settings("hejsan", 6u, "%s", "hej", NULL) == 0); + + s = NULL; + EXPECT(librecrypt_scan_settings("hej.", 4u, "%^s.", &s, "hej", "tja", "", NULL) == 1); + EXPECT(s && !strcmp(s, "hej")); + s = NULL; + EXPECT(librecrypt_scan_settings("tja.", 4u, "%^s.", &s, "hej", "tja", "", NULL) == 1); + EXPECT(s && !strcmp(s, "tja")); + s = NULL; + EXPECT(librecrypt_scan_settings(".", 1u, "%^s.", &s, "hej", "tja", "", NULL) == 1); + EXPECT(s && !strcmp(s, "")); + + EXPECT(librecrypt_scan_settings("10.", 3u, "%p.", RANGE(1, 10)) == 1); + EXPECT(librecrypt_scan_settings("10.", 3u, "%p.", RANGE(10, 20)) == 1); + EXPECT(librecrypt_scan_settings("10.", 3u, "%p.", RANGE(1, 20)) == 1); + EXPECT(librecrypt_scan_settings("10.", 3u, "%p.", RANGE(1, 9)) == 0); + EXPECT(librecrypt_scan_settings("10.", 3u, "%p.", RANGE(11, 20)) == 0); + EXPECT(librecrypt_scan_settings("0.", 2u, "%p.", RANGE(1, 10)) == 0); + EXPECT(librecrypt_scan_settings("0.", 2u, "%p.", RANGE(0, 10)) == 1); + EXPECT(librecrypt_scan_settings("00.", 3u, "%p.", RANGE(0, 10)) == 0); + EXPECT(librecrypt_scan_settings("010.", 4u, "%p.", RANGE(1, 10)) == 0); + + EXPECT(librecrypt_scan_settings("10.", 3u, "%u.", RANGE(1, 10)) == 1); + EXPECT(librecrypt_scan_settings("10.", 3u, "%u.", RANGE(10, 20)) == 1); + EXPECT(librecrypt_scan_settings("10.", 3u, "%u.", RANGE(1, 20)) == 1); + EXPECT(librecrypt_scan_settings("10.", 3u, "%u.", RANGE(1, 9)) == 0); + EXPECT(librecrypt_scan_settings("10.", 3u, "%u.", RANGE(11, 20)) == 0); + EXPECT(librecrypt_scan_settings("0.", 2u, "%u.", RANGE(1, 10)) == 0); + EXPECT(librecrypt_scan_settings("0.", 2u, "%u.", RANGE(0, 10)) == 1); + EXPECT(librecrypt_scan_settings("00.", 3u, "%u.", RANGE(0, 10)) == 1); + EXPECT(librecrypt_scan_settings("010.", 4u, "%u.", RANGE(1, 10)) == 1); + + u = 99u; + EXPECT(librecrypt_scan_settings("10.", 3u, "%^p.", &u, RANGE(0, 100)) == 1); + EXPECT(u == 10u); + EXPECT(librecrypt_scan_settings("15.", 3u, "%^p.", &u, RANGE(0, 100)) == 1); + EXPECT(u == 15u); + EXPECT(librecrypt_scan_settings("0.", 2u, "%^p.", &u, RANGE(0, 100)) == 1); + EXPECT(u == 0u); + + u = 99u; + EXPECT(librecrypt_scan_settings("10.", 3u, "%^u.", &u, RANGE(0, 100)) == 1); + EXPECT(u == 10u); + EXPECT(librecrypt_scan_settings("15.", 3u, "%^u.", &u, RANGE(0, 100)) == 1); + EXPECT(u == 15u); + EXPECT(librecrypt_scan_settings("0.", 2u, "%^u.", &u, RANGE(0, 100)) == 1); + EXPECT(u == 0u); + EXPECT(librecrypt_scan_settings("010.", 4u, "%^u.", &u, RANGE(0, 100)) == 1); + EXPECT(u == 10u); + EXPECT(librecrypt_scan_settings("015.", 4u, "%^u.", &u, RANGE(0, 100)) == 1); + EXPECT(u == 15u); + EXPECT(librecrypt_scan_settings("00.", 3u, "%^u.", &u, RANGE(0, 100)) == 1); + EXPECT(u == 0u); + + EXPECT(librecrypt_scan_settings("10.15.", 6u, "%u.%u.", RANGE(1, 10), RANGE(11, 20)) == 1); + EXPECT(librecrypt_scan_settings("10.10.", 6u, "%u.%u.", RANGE(1, 10), RANGE(11, 20)) == 0); + EXPECT(librecrypt_scan_settings("15.10.", 6u, "%u.%u.", RANGE(1, 10), RANGE(11, 20)) == 0); + + u = 99u; + u2 = 99u; + EXPECT(librecrypt_scan_settings("10.15.", 6u, "%^u.%^u.", &u, RANGE(1, 10), &u2, RANGE(11, 20)) == 1); + EXPECT(u == 10u); + EXPECT(u2 == 15u); + + EXPECT(librecrypt_scan_settings("", 0u, "%p", RANGE(0, 10)) == 0); + EXPECT(librecrypt_scan_settings("a", 1u, "%p", RANGE(0, 10)) == 0); + EXPECT(librecrypt_scan_settings("", 0u, "%u", RANGE(0, 10)) == 0); + EXPECT(librecrypt_scan_settings("a", 1u, "%u", RANGE(0, 10)) == 0); + EXPECT(librecrypt_scan_settings("", 0u, "%^p", &u, RANGE(0, 10)) == 0); + EXPECT(librecrypt_scan_settings("a", 1u, "%^p", &u, RANGE(0, 10)) == 0); + EXPECT(librecrypt_scan_settings("", 0u, "%^u", &u, RANGE(0, 10)) == 0); + EXPECT(librecrypt_scan_settings("a", 1u, "%^u", &u, RANGE(0, 10)) == 0); + + EXPECT(librecrypt_scan_settings("hej.hello.", 10u, "%s.%s.", "hej", NULL, "hello", NULL) == 1); + EXPECT(librecrypt_scan_settings("hello.hej.", 10u, "%s.%s.", "hej", NULL, "hello", NULL) == 0); + + EXPECT(librecrypt_scan_settings("hejsan", 1u, "%^s", &s, "hej", NULL) == 0); + s = NULL; + EXPECT(librecrypt_scan_settings("hejsan", 3u, "%^s", &s, "hej", NULL) == 1); + EXPECT(s && !strcmp(s, "hej")); + EXPECT(librecrypt_scan_settings("hejsan", 6u, "%^s", &s, "hej", NULL) == 0); + + s = NULL; + s2 = NULL; + EXPECT(librecrypt_scan_settings("hej.hello.", 10u, "%^s.%^s.", &s, "x", "hej", NULL, &s2, "y", "hello", NULL) == 1); + EXPECT(s && !strcmp(s, "hej")); + EXPECT(s2 && !strcmp(s2, "hello")); + + s = NULL; + s2 = NULL; + EXPECT(librecrypt_scan_settings("x.y.", 4u, "%^s.%^s.", &s, "x", "hej", NULL, &s2, "y", "hello", NULL) == 1); + EXPECT(s && !strcmp(s, "x")); + EXPECT(s2 && !strcmp(s2, "y")); + + check_asterisk('-', 0); + check_asterisk('-', 1); + check_asterisk('\0', 0); + check_asterisk('\0', 1); + + check_base64('-', 0); + check_base64('-', 1); + check_base64('\0', 0); + check_base64('\0', 1); + +#define S(STR) (STR), (sizeof(STR) - 1u) +#define LARGE "999999999999999999999999999999999999999" + EXPECT(librecrypt_scan_settings(S(LARGE LARGE LARGE LARGE), "%p", RANGE(0, UINTMAX_MAX)) == 0); + + EXPECT_ABORT(discarded_int = librecrypt_scan_settings("", 0u, "%\xFF", 0, 0, 0, 0, 0, 0, 0)); + + STOP_RESOURCE_TEST(); + return 0; +} + + +#endif diff --git a/librecrypt_set_custom_algorithms.3 b/librecrypt_set_custom_algorithms.3 new file mode 100644 index 0000000..a3a6dff --- /dev/null +++ b/librecrypt_set_custom_algorithms.3 @@ -0,0 +1,70 @@ +.TH LIBRECRYPT_SET_CUSTOM_ALGORITHMS 3 LIBRECRYPT +.SH NAME +librecrypt_set_custom_algorithms - Set application-provided hash algorithms + +.SH SYNOPSIS +.nf +#include + +struct librecrypt_algorithm { + /* Refer to for details! */ +}; + +void \fBlibrecrypt_set_custom_algorithms\fP(LIBRECRYPT_CONTEXT *\fIctx\fP, + const struct librecrypt_algorithm *\fIalgos\fP, + size_t \fInalgos\fP); + +.fi +.PP +Link with +.IR -lrecrypt . + +.SH DESCRIPTION +The +.BR librecrypt_set_custom_algorithms () +function stores to +.I ctx +which application-provided hash algorithms +to use, when +.I ctx +is used. +.PP +.I algos +shall be an array of hash algorithm +implementations, and +.I nalgos +shall be the number of hash algorithms +in the array. +.PP +Note that this resets setup of +custom hash algorithm on +.I *ctx. +.PP +The caller is responsible for the lifetime of +.IR algos : +deallocating it will deallocate it for +.I *ctx +as it only holds a reference to +.IR algos , +not a copy of it. + +.SH RETURN VALUES +None. + +.SH ERRORS +The +.BR librecrypt_set_custom_algorithms () +function cannot fail. + +.SH HISTORY +The +.BR librecrypt_set_custom_algorithms () +function was introduced in version 1.1 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_create_context (3) +.BR librecrypt_set_user_data (3), +.BR librecrypt_get_user_data (3), +.BR librecrypt_scan_settings (3) diff --git a/librecrypt_set_custom_algorithms.c b/librecrypt_set_custom_algorithms.c new file mode 100644 index 0000000..2494490 --- /dev/null +++ b/librecrypt_set_custom_algorithms.c @@ -0,0 +1,51 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +void +librecrypt_set_custom_algorithms(LIBRECRYPT_CONTEXT *ctx, const struct librecrypt_algorithm *algos, size_t nalgos) +{ + ctx->algos = algos; + ctx->nalgos = nalgos; +} + + +#else + + +int +main(void) +{ + LIBRECRYPT_CONTEXT *ctx; + const struct librecrypt_algorithm algos[5]; + + SET_UP_ALARM(); + INIT_RESOURCE_TEST(); + + ctx = librecrypt_create_context(); + assert(ctx != NULL); + + EXPECT(ctx->algos == NULL); + EXPECT(ctx->nalgos == 0u); + librecrypt_set_custom_algorithms(ctx, algos, 5u); + EXPECT(ctx->algos == algos); + EXPECT(ctx->nalgos == 5u); + librecrypt_set_custom_algorithms(ctx, algos, 5u); + EXPECT(ctx->algos == algos); + EXPECT(ctx->nalgos == 5u); + librecrypt_set_custom_algorithms(ctx, algos, 4u); + EXPECT(ctx->algos == algos); + EXPECT(ctx->nalgos == 4u); + librecrypt_set_custom_algorithms(ctx, NULL, 0u); + EXPECT(ctx->algos == NULL); + EXPECT(ctx->nalgos == 0u); + + librecrypt_free_context(ctx); + + STOP_RESOURCE_TEST(); + return 0; +} + + +#endif diff --git a/librecrypt_set_pepper.3 b/librecrypt_set_pepper.3 new file mode 100644 index 0000000..2d0c86b --- /dev/null +++ b/librecrypt_set_pepper.3 @@ -0,0 +1,137 @@ +.TH LIBRECRYPT_SET_PEPPER 3 LIBRECRYPT +.SH NAME +librecrypt_set_pepper - Set the pepper to use for a hash algorithm + +.SH SYNOPSIS +.nf +#include + +enum librecrypt_hash_algorithm { + LIBRECRYPT_ARGON2I_V1_0, + LIBRECRYPT_ARGON2I_V1_3, + LIBRECRYPT_ARGON2D_V1_0, + LIBRECRYPT_ARGON2D_V1_3, + LIBRECRYPT_ARGON2ID_V1_0, + LIBRECRYPT_ARGON2ID_V1_3, + LIBRECRYPT_ARGON2DS_V1_0, + LIBRECRYPT_ARGON2DS_V1_3, + LIBRECRYPT_HASH_ALGORITHM_END /* end of enum marker (always last) */ +}; + +void \fBlibrecrypt_set_pepper\fP(LIBRECRYPT_CONTEXT *\fIctx\fP, enum librecrypt_hash_algorithm \fIalgo\fP, + const void *\fIdata\fP, size_t \fIlen\fP); +.fi +.PP +Link with +.IR -lrecrypt . + +.SH DESCRIPTION +The +.BR librecrypt_set_pepper () +function sets the pepper for a library-provided +hash algorithm selected by the +.I algo +argument, when the +.I ctx +object is specified when performing the has. +.PP +The +.I data +argument shall be the pepper in raw binary, +and the +.I len +argument shall be the number of bytes in +.IR data . +.PP +The caller is responsible for the lifetime of +.IR data : +deallocating it will deallocate it for +.I *ctx +as it only holds a reference to +.IR data , +not a copy of it. + +.SH RETURN VALUES +The +.BR librecrypt_set_pepper () +function returns 0 upon successful completion. +On failure, -1 is returned and +.I errno +is set to describe the error. + +.SH ERRORS +The +.BR librecrypt_set_pepper () +function will fail if: +.TP +.B EINVAL +The value of +.I len +is not supported for the selected algorithm. +(This implies that the selected algorithm +was enabled at compile time and that it +supports pepper.) +.TP +.B ENOSUP +The algorithm selected by the +.I algo +argument does not support peppers. +(This implies that the selected algorithm +was enabled at compile time.) +.TP +.B ENOSYS +The algorithm selected by the +.I algo +argument is either not recognised by the +library or was disabled at compile-time. + +.SH EXTENDED DESCRIPTION +The following values for +.I algo +are supported and may cause the +.BR librecrypt_is_enabled () +function to return either 0 or 1 +(all other cause the function to return 0): +.TP +.B LIBRECRYPT_ARGON2I_V1_0 +Argon2i, version 1.0, which is presented by +\(dq$argon2i$v=13$\(dq, optionally without \(dq$v=13\(dq. +.TP +.B LIBRECRYPT_ARGON2I_V1_3 +Argon2i, version 1.3, which is presented by +\(dq$argon2i$v=19$\(dq. +.TP +.B LIBRECRYPT_ARGON2D_V1_0 +Argon2d, version 1.0, which is presented by +\(dq$argon2d$v=13$\(dq, optionally without \(dq$v=13\(dq. +.TP +.B LIBRECRYPT_ARGON2D_V1_3 +Argon2d, version 1.3, which is presented by +\(dq$argon2d$v=19$\(dq. +.TP +.B LIBRECRYPT_ARGON2ID_V1_0 +Argon2id, version 1.0, which is presented by +\(dq$argon2id$v=13$\(dq, optionally without \(dq$v=13\(dq. +.TP +.B LIBRECRYPT_ARGON2ID_V1_3 +Argon2id, version 1.3, which is presented by +\(dq$argon2id$v=19$\(dq. +.TP +.B LIBRECRYPT_ARGON2DS_V1_0 +Argon2ds, version 1.0, which is presented by +\(dq$argon2ds$v=13$\(dq, optionally without \(dq$v=13\(dq. +.TP +.B LIBRECRYPT_ARGON2DS_V1_3 +Argon2ds, version 1.3, which is presented by +\(dq$argon2ds$v=19$\(dq. + +.SH HISTORY +The +.BR librecrypt_set_pepper () +function was introduced in version 1.1 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_create_context (3), +.BR librecrypt_hash_algorithm_end (3) diff --git a/librecrypt_set_pepper.c b/librecrypt_set_pepper.c new file mode 100644 index 0000000..966563c --- /dev/null +++ b/librecrypt_set_pepper.c @@ -0,0 +1,143 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +librecrypt_set_pepper(LIBRECRYPT_CONTEXT *ctx, enum librecrypt_hash_algorithm algo, const void *data, size_t len) +{ + struct pepper *pepper; + + pepper = librecrypt_get_pepper_(ctx, algo, len); + if (!pepper) + return -1; + + pepper->data = data; + pepper->len = len; + return 0; +} + + +#else + + +#define CHECK_ENOSYS(ALGO, DATA, LEN)\ + do {\ + errno = 0;\ + EXPECT(librecrypt_set_pepper(ctx, ALGO, DATA, LEN) == -1);\ + EXPECT(errno == ENOSYS);\ + } while (0) + +#define CHECK_EINVAL(ALGO, DATA, LEN)\ + do {\ + errno = 0;\ + EXPECT(librecrypt_set_pepper(ctx, ALGO, DATA, LEN) == -1);\ + EXPECT(errno == EINVAL);\ + } while (0) + +#define SET_PEPPER(ALGO, DATA, LEN)\ + EXPECT(librecrypt_set_pepper(ctx, ALGO, DATA, LEN) == 0) + +#define CHECK_PEPPER(ALGO, DATA, LEN)\ + do {\ + pepper = librecrypt_get_pepper_(ctx, ALGO, 0u);\ + EXPECT(pepper != NULL);\ + EXPECT(pepper->data == (DATA));\ + EXPECT(pepper->len == (LEN));\ + } while (0) + + +int +main(void) +{ + LIBRECRYPT_CONTEXT *ctx; + char a2i10 = 1, a2d10 = 2, a2id10 = 3, a2ds10 = 4; + char a2i13 = 5, a2d13 = 6, a2id13 = 7, a2ds13 = 8; + struct pepper *pepper; + + SET_UP_ALARM(); + INIT_RESOURCE_TEST(); + + ctx = librecrypt_create_context(); + assert(ctx != NULL); + + +#if defined(SUPPORT_ARGON2I) + SET_PEPPER(LIBRECRYPT_ARGON2I_V1_0, &a2i10, 2u); + SET_PEPPER(LIBRECRYPT_ARGON2I_V1_3, &a2i13, 5u); +# if SIZE_MAX > UINT32_MAX + CHECK_EINVAL(LIBRECRYPT_ARGON2I_V1_0, NULL, (size_t)UINT32_MAX + 1u); + CHECK_EINVAL(LIBRECRYPT_ARGON2I_V1_3, NULL, (size_t)UINT32_MAX + 1u); +# endif +#else + CHECK_ENOSYS(LIBRECRYPT_ARGON2I_V1_0, &a2i10, 2u); + CHECK_ENOSYS(LIBRECRYPT_ARGON2I_V1_3, &a2i13, 5u); +#endif + +#if defined(SUPPORT_ARGON2D) + SET_PEPPER(LIBRECRYPT_ARGON2D_V1_0, &a2d10, 3u); + SET_PEPPER(LIBRECRYPT_ARGON2D_V1_3, &a2d13, 8u); +# if SIZE_MAX > UINT32_MAX + CHECK_EINVAL(LIBRECRYPT_ARGON2D_V1_0, NULL, (size_t)UINT32_MAX + 1u); + CHECK_EINVAL(LIBRECRYPT_ARGON2D_V1_3, NULL, (size_t)UINT32_MAX + 1u); +# endif +#else + CHECK_ENOSYS(LIBRECRYPT_ARGON2D_V1_0, &a2d10, 3u); + CHECK_ENOSYS(LIBRECRYPT_ARGON2D_V1_3, &a2d13, 8u); +#endif + +#if defined(SUPPORT_ARGON2ID) + SET_PEPPER(LIBRECRYPT_ARGON2ID_V1_0, &a2id10, 7u); + SET_PEPPER(LIBRECRYPT_ARGON2ID_V1_3, &a2id13, 4u); +# if SIZE_MAX > UINT32_MAX + CHECK_EINVAL(LIBRECRYPT_ARGON2ID_V1_0, NULL, (size_t)UINT32_MAX + 1u); + CHECK_EINVAL(LIBRECRYPT_ARGON2ID_V1_3, NULL, (size_t)UINT32_MAX + 1u); +# endif +#else + CHECK_ENOSYS(LIBRECRYPT_ARGON2ID_V1_0, &a2id10, 7u); + CHECK_ENOSYS(LIBRECRYPT_ARGON2ID_V1_3, &a2id13, 4u); +#endif + +#if defined(SUPPORT_ARGON2DS) + SET_PEPPER(LIBRECRYPT_ARGON2DS_V1_0, &a2ds10, 9u); + SET_PEPPER(LIBRECRYPT_ARGON2DS_V1_3, &a2ds13, 6u); +# if SIZE_MAX > UINT32_MAX + CHECK_EINVAL(LIBRECRYPT_ARGON2DS_V1_0, NULL, (size_t)UINT32_MAX + 1u); + CHECK_EINVAL(LIBRECRYPT_ARGON2DS_V1_3, NULL, (size_t)UINT32_MAX + 1u); +# endif +#else + CHECK_ENOSYS(LIBRECRYPT_ARGON2DS_V1_0, &a2ds10, 9u); + CHECK_ENOSYS(LIBRECRYPT_ARGON2DS_V1_3, &a2ds13, 6u); +#endif + + + CHECK_ENOSYS(LIBRECRYPT_HASH_ALGORITHM_END, NULL, 0u); + CHECK_ENOSYS(LIBRECRYPT_HASH_ALGORITHM_END, &(char){4}, 32u); + + +#if defined(SUPPORT_ARGON2I) + CHECK_PEPPER(LIBRECRYPT_ARGON2I_V1_0, &a2i10, 2u); + CHECK_PEPPER(LIBRECRYPT_ARGON2I_V1_3, &a2i13, 5u); +#endif +#if defined(SUPPORT_ARGON2D) + CHECK_PEPPER(LIBRECRYPT_ARGON2D_V1_0, &a2d10, 3u); + CHECK_PEPPER(LIBRECRYPT_ARGON2D_V1_3, &a2d13, 8u); +#endif +#if defined(SUPPORT_ARGON2ID) + CHECK_PEPPER(LIBRECRYPT_ARGON2ID_V1_0, &a2id10, 7u); + CHECK_PEPPER(LIBRECRYPT_ARGON2ID_V1_3, &a2id13, 4u); +#endif +#if defined(SUPPORT_ARGON2DS) + CHECK_PEPPER(LIBRECRYPT_ARGON2DS_V1_0, &a2ds10, 9u); + CHECK_PEPPER(LIBRECRYPT_ARGON2DS_V1_3, &a2ds13, 6u); +#endif + + + librecrypt_free_context(ctx); + + STOP_RESOURCE_TEST(); + return 0; +} + + +#endif diff --git a/librecrypt_set_user_data.3 b/librecrypt_set_user_data.3 new file mode 100644 index 0000000..8983ea0 --- /dev/null +++ b/librecrypt_set_user_data.3 @@ -0,0 +1,49 @@ +.TH LIBRECRYPT_SET_USER_DATA 3 LIBRECRYPT +.SH NAME +librecrypt_set_user_data - Set application-defined data + +.SH SYNOPSIS +.nf +#include + +void \fBlibrecrypt_set_user_data\fP(LIBRECRYPT_CONTEXT *\fIctx\fP, void *\fIuser\fP); +.fi +.PP +Link with +.IR -lrecrypt . + +.SH DESCRIPTION +The +.BR librecrypt_set_user_data () +function stores a pointer to application-defined +data, provided via the +.I user +parameter, inside the +.IR ctx . +.PP +The caller is responsible for the lifetime of +.IR user : +deallocating it will deallocate it for +.I *ctx +as it only holds a reference to +.IR user , +not a copy of it. + +.SH RETURN VALUES +None. + +.SH ERRORS +The +.BR librecrypt_set_user_data () +function cannot fail. + +.SH HISTORY +The +.BR librecrypt_set_user_data () +function was introduced in version 1.1 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_get_user_data (3), +.BR librecrypt_set_custom_algorithms (3) diff --git a/librecrypt_set_user_data.c b/librecrypt_set_user_data.c new file mode 100644 index 0000000..f2abb2f --- /dev/null +++ b/librecrypt_set_user_data.c @@ -0,0 +1,49 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +void +librecrypt_set_user_data(LIBRECRYPT_CONTEXT *ctx, void *user) +{ + ctx->user_data = user; +} + + +#else + + +extern LIBRECRYPT_CONTEXT *volatile ctx; +LIBRECRYPT_CONTEXT *volatile ctx; + + +int +main(void) +{ + char *user = &(char){0}; + + SET_UP_ALARM(); + INIT_RESOURCE_TEST(); + + ctx = librecrypt_create_context(); + assert(ctx != NULL); + + EXPECT(librecrypt_get_user_data(ctx) == NULL); + EXPECT(librecrypt_get_user_data(ctx) == NULL); + librecrypt_set_user_data(ctx, user); + EXPECT(librecrypt_get_user_data(ctx) == user); + EXPECT(librecrypt_get_user_data(ctx) == user); + librecrypt_set_user_data(ctx, user); + librecrypt_set_user_data(ctx, NULL); + EXPECT(librecrypt_get_user_data(ctx) == NULL); + EXPECT(librecrypt_get_user_data(ctx) == NULL); + librecrypt_set_user_data(ctx, NULL); + + librecrypt_free_context(ctx); + + STOP_RESOURCE_TEST(); + return 0; +} + + +#endif diff --git a/librecrypt_settings_prefix.3 b/librecrypt_settings_prefix.3 index 8e425a8..d20c6a1 100644 --- a/librecrypt_settings_prefix.3 +++ b/librecrypt_settings_prefix.3 @@ -7,7 +7,7 @@ librecrypt_settings_prefix - Get length of settings prefix in a password hash st #include size_t \fBlibrecrypt_settings_prefix\fP(const char *\fIhash\fP, size_t *\fIhashsize_out\fP, - LIBRECRYPT_CONTEXT *\fIctx\fP); + LIBRECRYPT_CONTEXT *\fIctx\fP); .fi .PP Link with diff --git a/librecrypt_settings_prefix.c b/librecrypt_settings_prefix.c index 89bb5ae..fbb8efe 100644 --- a/librecrypt_settings_prefix.c +++ b/librecrypt_settings_prefix.c @@ -36,12 +36,12 @@ librecrypt_settings_prefix(const char *hash, size_t *hashsize_out, LIBRECRYPT_CO if (!algo) goto zero; if (!algo->flexible_hash_size) - goto zero; /* $covered$ (TODO we currently don't have an algorithm to trigger this) */ + goto zero; /* TODO test with custom hash function */ /* Get the hash size */ - if (!librecrypt_scan_settings_(&hash[ret], len - ret, "%^b", - &hashsize, (uintmax_t)1u, (uintmax_t)SIZE_MAX, - algo->decoding_lut, algo->pad, algo->strict_pad)) + if (!librecrypt_scan_settings(&hash[ret], len - ret, "%^b", + &hashsize, (uintmax_t)1u, (uintmax_t)SIZE_MAX, + algo->decoding_lut, algo->pad, algo->strict_pad)) goto zero; *hashsize_out = (size_t)hashsize; -- cgit v1.3.1