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 --- common.h | 246 +++------------------------------------------------------------ 1 file changed, 11 insertions(+), 235 deletions(-) (limited to 'common.h') 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); -- cgit v1.3.1