diff options
| author | Mattias Andrée <m@maandree.se> | 2026-05-21 17:12:20 +0200 |
|---|---|---|
| committer | Mattias Andrée <m@maandree.se> | 2026-05-21 17:12:20 +0200 |
| commit | b29f4153e83623f24bebe99976e1368ef31bb008 (patch) | |
| tree | 65473709df1194a2f9277dc5fb47add5e41430c0 /librecrypt.h | |
| parent | Add (so far untested and undocument) support for pepper (diff) | |
| download | librecrypt-b29f4153e83623f24bebe99976e1368ef31bb008.tar.gz librecrypt-b29f4153e83623f24bebe99976e1368ef31bb008.tar.bz2 librecrypt-b29f4153e83623f24bebe99976e1368ef31bb008.tar.xz | |
Add support for custom hash functions
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to '')
| -rw-r--r-- | librecrypt.h | 306 |
1 files changed, 291 insertions, 15 deletions
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__) |
