diff options
| author | Mattias Andrée <m@maandree.se> | 2026-04-26 22:36:47 +0200 |
|---|---|---|
| committer | Mattias Andrée <m@maandree.se> | 2026-04-26 22:36:47 +0200 |
| commit | d77ab463184d113ca6119403887c9f4ed0dfdf0b (patch) | |
| tree | ca8a1de443f90a4b7def56ea5b61c96aaa949f45 | |
| download | librecrypt-d77ab463184d113ca6119403887c9f4ed0dfdf0b.tar.gz librecrypt-d77ab463184d113ca6119403887c9f4ed0dfdf0b.tar.bz2 librecrypt-d77ab463184d113ca6119403887c9f4ed0dfdf0b.tar.xz | |
First commit
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to '')
53 files changed, 5416 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bb11397 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +*\#* +*~ +*.o +*.a +*.t +*.lo +*.to +*.su +*.so +*.so.* +*.dll +*.dylib +*.gch +*.gcov +*.gcno +*.gcda @@ -0,0 +1,15 @@ +ISC License + +© 2026 Mattias Andrée <m@maandree.se> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..549b8fb --- /dev/null +++ b/Makefile @@ -0,0 +1,126 @@ +.POSIX: + +CONFIGFILE = config.mk +include $(CONFIGFILE) + +OS = linux +# Linux: linux +# Mac OS: macos +# Windows: windows +include mk/$(OS).mk + + +LIB_MAJOR = 1 +LIB_MINOR = 0 +LIB_VERSION = $(LIB_MAJOR).$(LIB_MINOR) +LIB_NAME = recrypt + + +OBJ_PUBLIC =\ + librecrypt_settings_prefix.o\ + librecrypt_chain_length.o\ + librecrypt_decompose_chain.o\ + librecrypt_decompose_chain1.o\ + librecrypt_next_algorithm.o\ + librecrypt_encode.o\ + librecrypt_decode.o\ + librecrypt_get_encoding.o\ + librecrypt_wipe.o\ + librecrypt_wipe_str.o\ + librecrypt_equal_binary.o\ + librecrypt_equal.o\ + librecrypt_realise_salts.o\ + librecrypt_make_settings.o\ + librecrypt_hash_binary.o\ + librecrypt_hash.o\ + librecrypt_crypt.o\ + librecrypt_add_algorithm.o\ + librecrypt_test_supported.o + +OBJ_PRIVATE =\ + librecrypt_hash_.o\ + librecrypt_rng_.o\ + librecrypt_fill_with_random_.o\ + librecrypt_find_first_algorithm_.o + +OBJ = $(OBJ_PUBLIC) $(OBJ_PRIVATE) + +HDR =\ + librecrypt.h\ + common.h\ + algorithms.h + +LOBJ = $(OBJ:.o=.lo) +TOBJ = $(OBJ:.o=.to) +TEST = $(OBJ:.o=.t) +MAN3 = $(OBJ_PUBLIC:.o=.3) +MAN7 = librecrypt.7 + + +all: librecrypt.a librecrypt.$(LIBEXT) $(TEST) +$(OBJ): $(HDR) +$(LOBJ): $(HDR) +$(TOBJ): $(HDR) +$(TEST): librecrypt.a + +.c.o: + $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS) + +.c.lo: + $(CC) -fPIC -c -o $@ $< $(CFLAGS) $(CPPFLAGS) + +.c.to: + $(CC) -DTEST -c -o $@ $< $(CFLAGS) $(CPPFLAGS) + +.to.t: + $(CC) -o $@ $< librecrypt.a $(LDFLAGS) + +.c.t: + $(CC) -DTEST -o $@ $< librecrypt.a $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) + +librecrypt.a: $(OBJ) + @rm -f -- $@ + $(AR) rc $@ $(OBJ) + $(AR) ts $@ > /dev/null + +librecrypt.$(LIBEXT): $(LOBJ) + $(CC) $(LIBFLAGS) -o $@ $(LOBJ) $(LDFLAGS) + +check: $(TEST) + @set -ex;\ + for t in $(TEST); do\ + $(CHECK_PREFIX) ./$$t;\ + done + +install: librecrypt.a librecrypt.$(LIBEXT) + mkdir -p -- "$(DESTDIR)$(PREFIX)/lib" + mkdir -p -- "$(DESTDIR)$(PREFIX)/include" + mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man3" + mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man7" + cp -- librecrypt.a "$(DESTDIR)$(PREFIX)/lib/" + cp -- librecrypt.$(LIBEXT) "$(DESTDIR)$(PREFIX)/lib/librecrypt.$(LIBMINOREXT)" + $(FIX_INSTALL_NAME) "$(DESTDIR)$(PREFIX)/lib/librecrypt.$(LIBMINOREXT)" + ln -sf -- librecrypt.$(LIBMINOREXT) "$(DESTDIR)$(PREFIX)/lib/librecrypt.$(LIBMAJOREXT)" + ln -sf -- librecrypt.$(LIBMAJOREXT) "$(DESTDIR)$(PREFIX)/lib/librecrypt.$(LIBEXT)" + cp -- librecrypt.h "$(DESTDIR)$(PREFIX)/include/" + cp -P -- $(MAN3) "$(DESTDIR)$(MANPREFIX)/man3/" + cp -P -- $(MAN7) "$(DESTDIR)$(MANPREFIX)/man7/" + +uninstall: + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/librecrypt.a" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/librecrypt.$(LIBMAJOREXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/librecrypt.$(LIBMINOREXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/librecrypt.$(LIBEXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/include/librecrypt.h" + -cd -- "$(DESTDIR)$(MANPREFIX)/man3/" && rm -f -- $(MAN3) + -cd -- "$(DESTDIR)$(MANPREFIX)/man7/" && rm -f -- $(MAN7) + +clean: + -rm -f -- *.o *.a *.lo *.su *.so *.so.* *.dll *.dylib + -rm -f -- *.gch *.gcov *.gcno *.gcda *.$(LIBEXT) + -rm -f -- *.to *.t + +.SUFFIXES: +.SUFFIXES: .lo .o .c .to .t + +.PHONY: all check install uninstall clean diff --git a/algorithms.h b/algorithms.h new file mode 100644 index 0000000..d38dcf5 --- /dev/null +++ b/algorithms.h @@ -0,0 +1,25 @@ +/* See LICENSE file for copyright and license details. */ +/* included from "common.h" */ + + +/* ordered by preference */ +#define LIST_ALGORITHMS(X) /* TODO add algorithms */ + + +#define Y(ALGO)\ + HIDDEN size_t librecrypt__##ALGO##__get_prefix(const char *settings, size_t len);\ + HIDDEN unsigned librecrypt__##ALGO##__is_algorithm(const char *settings, size_t len);\ + HIDDEN int librecrypt__##ALGO##__hash(char *restrict out_buffer, size_t size, const char *phrase,\ + size_t len, const char *settings, size_t prefix, void *reserved);\ + HIDDEN int librecrypt__##ALGO##__test_supported(const char *phrase, size_t len, int text,\ + const char *settings, size_t prefix);\ + HIDDEN ssize_t librecrypt__##ALGO##__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);\ + NONSTRING extern const char librecrypt__##ALGO##__encoding_lut[256];\ + extern const unsigned char librecrypt__##ALGO##__decoding_lut[256]; + +#define X(ALGO) IF__##ALGO##__SUPPORTED(Y(ALGO)) +LIST_ALGORITHMS(X) +#undef X +#undef Y diff --git a/common.h b/common.h new file mode 100644 index 0000000..389244f --- /dev/null +++ b/common.h @@ -0,0 +1,426 @@ +/* See LICENSE file for copyright and license details. */ +#include "librecrypt.h" +#if defined(__linux__) +# include <sys/auxv.h> +# include <sys/random.h> +#endif +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +#include "algorithms.h" + + +#if defined(__GNUC__) +# define HIDDEN __attribute__((__visibility__("hidden"))) +# define CONST __attribute__((__const__)) +#else +# define HIDDEN +# define CONST +#endif + +#define NONSTRING +#if defined(__GNUC__) && !defined(__clang__) +# if __GNUC__ >= 8 +# undef NONSTRING +# define NONSTRING __attribute__((__nonstring__)) +# endif +#endif + + +/** + * Used for literal commas in macro calls + */ +#define COMMA , + +/** + * Get the number of elements in an array + * + * @param ARRAY:nondecayed-array The array + * @return :size_t The number of elements in `ARRAY` + */ +#define ELEMSOF(ARRAY) (sizeof(ARRAY) / sizeof(*(ARRAY))) + + +/** + * Which function `librecrypt_hash_` shall behave as + */ +enum action { + /** + * Used for `librecrypt_hash_binary` + * + * Output is binary and hash result-only + */ + BINARY_HASH, + + /** + * Used for `librecrypt_hash` + * + * Output is ASCII and hash result-only + */ + ASCII_HASH, + + /** + * Used for `librecrypt_hash` + * + * Output is ASCII and contains algorithm configurations + */ + ASCII_CRYPT +}; + + +/** + * Hash algorithm information and implementation + */ +struct algorithm { + /** + * Get the number of bytes that constitute + * the algorithm specification and configuration + * + * @param settings The password hash string, containing a single algorithm + * @param len The number of bytes in `settings` + * @return `len` if the string does not contain any hash result, + * otherwise the offset of `settings` where the hash + * result begins + * + * This function shall be MT-Safe and AS-Safe + */ + size_t (*get_prefix)(const char *settings, size_t len); + + /** + * 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 reserved 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, void *reserved); + + /** + * 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 + * @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); + + /** + * 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` + * @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 + */ + const char *encoding_lut; + + /** + * Expected argument for the `lut` parameter + * of the `librecrypt_decode` function + */ + const unsigned char *decoding_lut; + + /** + * The algoritm's hash result size, in number + * of bytes when using binary encoding + */ + size_t hash_size; + + /** + * Expected argument for the `strict_pad` parameter + * of the `librecrypt_decode` function + */ + int strict_pad; + + /** + * Expected argument for the `pad` parameter + * of the `librecrypt_decode` function + */ + char pad; +}; + +/** + * Check if an `struct algorithm *` is the `END_OF_ALGORITHMS` + * at the end of `librecrypt_algorithms_` + */ +#define IS_END_OF_ALGORITHMS(A) (!(A)->get_prefix) + +/** + * Used at the end of `librecrypt_algorithms_` + */ +#define END_OF_ALGORITHMS {NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, '\0'} + +/** + * Create a concatenation of `ALPHABET` repeated + * for times; used to convert a base-64 alphabet + * to an encoding lookup table + * + * @param ALPHABET:string-literal A 64-character string + * @return :string-literal A 256-character string + * + * @seealso NONSTRING + */ +#define MAKE_ENCODING_LUT(ALPHABET) ALPHABET ALPHABET ALPHABET ALPHABET + +/** + * Use in algorithms's base-64 decoding lookup tables + * in entries matching the pad character and invalid + * characters + */ +#define XX 0xFFu + + +/** + * The list of all supported algorithms (those not disabled + * at compile-time) + * + * They are ordered by how preferable they are as the + * default algorithm, with the most preferable first + * + * The list is terminated by `END_OF_ALGORITHMS`, + * which can be checked using `IS_END_OF_ALGORITHMS` + */ +extern struct algorithm librecrypt_algorithms_[]; + +/** + * This just points to memset(3), but the pointer is volalite + * so that the compiler cannot assume that, and therefore the + * call cannot be optimised away + */ +extern void *(*volatile librecrypt_explicit_memset_____)(void *, int, size_t); /* librecrypt_wipe.c */ + +/** + * The is a pointer to a function that doesn't do anything, + * however the pointer is volatile so that the function + * cannot assume that and cannot optimise away the calculating + * of the input value + */ +extern void (*volatile librecrypt_explicit_____)(unsigned char); /* librecrypt_equal_binary.c */ + + +/** + * This function implements `librecrypt_hash_binary`, `librecrypt_hash`, + * and `librecrypt_test_supported`, depending on the value of `action`, + * see documentation of those functions for more default information + * + * @param out_buffer Output buffer for the hash result + * @param size The number bytes the function may write to `out_buffer` + * @param phrase The password to hash, may contain NUL bytes + * @param len The number of bytes in `phrase` + * @param settings The password hash configuration string, + * may contain resulting hash, which will be ignored + * @param reserved Reserved for future use, should be `NULL` + * @param action The function this function shall implement + * @return The number of bytes that would have been written to `out_buffer` + * if `size` was sufficiently large, excluding a terminating + * NUL byte (not present if `action == BINARY_HASH`); + * -1 on failure + * + * This function is MT-Safe but AS-Unsafe + */ +LIBRECRYPT_WRITE_MEM__(1, 2) LIBRECRYPT_READ_MEM__(3, 4) +LIBRECRYPT_READ_STR__(6) LIBRECRYPT_NONNULL_I__(5) LIBRECRYPT_WUR__ HIDDEN +ssize_t librecrypt_hash_(char *restrict out_buffer, size_t size, const char *phrase, size_t len, + const char *settings, void *reserved, enum action action); + + +/** + * Default random number generator, used if no other + * is specified + * + * Generates up to `n`, or {SSIZE_MAX} if `n` is + * greater than {SSIZE_MAX}, random bytes + * + * @param out Output buffer for the random bytes + * @param n The maximum number of bytes to generate + * @param user Not used + * @return The number of generated bytes; + * will always be positive + * + * This function cannot fail, however it will ignore + * any `EINTR`; if `EINTR` is encountered `errno` will + * be set to `EINTR` upon return, otherwise `errno` + * will remain unmodifed + * + * This function is MT-Safe but AS-Unsafe + */ +LIBRECRYPT_WRITE_MEM__(1, 2) LIBRECRYPT_WUR__ HIDDEN +ssize_t librecrypt_rng_(void *out, size_t n, void *user); + + +/** + * Generate a specific number of random bytes + * + * @param out Output buffer for the random bytes + * @param n The number of bytes to generate + * @param rng The random number generator to use, + * `librecrypt_rng_` will be used if `NULL`, + * see `librecrypt_rng_` details, but not + * that it may return -1 on failure and has + * no restrictions on modifying `errno` + * @param user Passed as is to `*rng` as its third argument, + * so that it can be used for user-defined + * purposes + * @return 0 on success, -1 on failure + * + * When `rng` is non-`NULL`, this function inherits any + * MT-Unsafe and AS-Unsafe properties from `*rng`, being + * is MT-Safe and AS-Safe as a baseline; however when + * `rng` is `NULL`, this function is MT-Safe but AS-Unsafe + */ +LIBRECRYPT_WRITE_MEM__(1, 2) LIBRECRYPT_WUR__ HIDDEN +int librecrypt_fill_with_random_(void *out, size_t n, ssize_t (*rng)(void *out, size_t n, void *user), void *user); + + +/** + * Find the first algorithm specified by a password has string + * + * @param settings The password has string + * @param len The number of bytes in `settings` + * @return Pointer to the algorithm information, + * `NULL` if not found + * + * This function does not modify `errno`, but the + * caller should usually set `errno` to `ENOSYS` + * if this function returns `NULL` + * + * This function is MT-Safe And AS-Safe + */ +LIBRECRYPT_READ_MEM__(1, 2) LIBRECRYPT_NONNULL__ LIBRECRYPT_WUR__ HIDDEN +const struct algorithm *librecrypt_find_first_algorithm_(const char *settings, size_t len); + + + +#ifdef TEST +# ifdef __linux__ +# include <sys/prctl.h> +# endif +# include <sys/resource.h> +# include <sys/types.h> +# include <sys/wait.h> +# include <assert.h> +# include <signal.h> +# include <stdio.h> +# include <string.h> +# include <unistd.h> + +# define SET_UP_ALARM()\ + do {\ + unsigned int alarm_time__ = alarm(10u);\ + if (alarm_time__ > 10u)\ + alarm(alarm_time__);\ + } while (0) + +# if defined(PR_SET_DUMPABLE) +# define INIT_TEST_ABORT()\ + do {\ + struct rlimit rl__;\ + rl__.rlim_cur = 0;\ + rl__.rlim_max = 0;\ + (void) setrlimit(RLIMIT_CORE, &rl__);\ + (void) prctl(PR_SET_DUMPABLE, 0);\ + EXPECT_ABORT(abort());\ + } while (0) +# else +# define INIT_TEST_ABORT()\ + do {\ + struct rlimit rl__;\ + rl__.rlim_cur = 0;\ + rl__.rlim_max = 0;\ + (void) setrlimit(RLIMIT_CORE, &rl__);\ + EXPECT_ABORT(abort());\ + } while (0) +# endif + +# define EXPECT__(EXPR, HOW, RETEXTRACT, RETEXPECT)\ + do {\ + pid_t pid__;\ + int status__;\ + pid__ = fork();\ + EXPECT(pid__ != -1);\ + if (pid__ == 0) {\ + (EXPR);\ + _exit(0);\ + }\ + EXPECT(waitpid(pid__, &status__, 0) == pid__);\ + EXPECT(HOW(status__));\ + EXPECT(RETEXTRACT(status__) == RETEXPECT);\ + } while (0) + +# define EXPECT_ABORT(EXPR)\ + do {\ + EXPECT__(EXPR, WIFSIGNALED, WTERMSIG, SIGABRT);\ + } while (0) + +# define EXPECT(EXPR)\ + do {\ + if (!(EXPR)) {\ + fprintf(stderr, "Failure at %s:%i: %s\n", __FILE__, __LINE__, #EXPR);\ + exit(1);\ + }\ + } while (0) +#endif diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..f4adf12 --- /dev/null +++ b/config.mk @@ -0,0 +1,8 @@ +PREFIX = /usr +MANPREFIX = $(PREFIX)/share/man + +CC = c99 + +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE +CFLAGS = +LDFLAGS = diff --git a/librecrypt.h b/librecrypt.h new file mode 100644 index 0000000..9cf966b --- /dev/null +++ b/librecrypt.h @@ -0,0 +1,776 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef LIBRECRYPT_H +#define LIBRECRYPT_H + +#include <sys/types.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + + +#if defined(__GNUC__) +# define LIBRECRYPT_PURE__ __attribute__((__pure__)) +# define LIBRECRYPT_NONNULL__ __attribute__((__nonnull__)) +# define LIBRECRYPT_NONNULL_I__(I) __attribute__((__nonnull__(I))) +# define LIBRECRYPT_WUR__ __attribute__((__warn_unused_result__)) +# 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))) +#else +# define LIBRECRYPT_PURE__ +# define LIBRECRYPT_NONNULL__ +# define LIBRECRYPT_NONNULL_I__(I) +# define LIBRECRYPT_WUR__ +# define LIBRECRYPT_READ_STR__(S) +# define LIBRECRYPT_READ_MEM__(B, N) +# define LIBRECRYPT_WRITE_MEM__(B, N) +# define LIBRECRYPT_READ_WRITE_STR__(S) +#endif +#define LIBRECRYPT_NONNULL_1__ LIBRECRYPT_NONNULL_I__(1) + + +/** + * Symbol used as a general delimiter + */ +#define LIBRECRYPT_HASH_COMPOSITION_DELIMITER '$' + +/** + * Symbol used to delimit algorithms in a chain + */ +#define LIBRECRYPT_ALGORITHM_LINK_DELIMITER '>' + + + +/** + * Get number of bytes in a password hash string + * that make up the algorithm configuration + * + * @param hash The password hash string; must not be `NULL` + * @return The number of bytes, from the front of `hash`, + * that make up the algorithm configuration; may be 0 + * + * For the return value `r`, `&hash[r]` points to the + * hash result proper + * + * This function is MT-Safe and AS-Safe + */ +LIBRECRYPT_READ_STR__(1) LIBRECRYPT_NONNULL__ LIBRECRYPT_WUR__ LIBRECRYPT_PURE__ +inline size_t +librecrypt_settings_prefix(const char *hash) +{ + size_t i, ret = 0u; + + for (i = 0u; hash[i]; i++) + if (hash[i] == LIBRECRYPT_HASH_COMPOSITION_DELIMITER || hash[i] == LIBRECRYPT_ALGORITHM_LINK_DELIMITER) + ret = i + 1u; + + return ret; +} + + +/** + * Get the number of chained hash algorithms specified in + * a password hash string + * + * @param hash The password hash string; must not be `NULL` + * @return The number of chained hash algorithms; always non-zero + * + * @seealso librecrypt_decompose_chain + * @seealso librecrypt_decompose_chain1 + * @seealso librecrypt_next_algorithm + * + * This function is MT-Safe and AS-Safe + */ +LIBRECRYPT_READ_STR__(1) LIBRECRYPT_NONNULL__ LIBRECRYPT_WUR__ LIBRECRYPT_PURE__ +inline size_t +librecrypt_chain_length(const char *hash) +{ + size_t n = 1u; + + for (; *hash; hash++) + if (*hash == LIBRECRYPT_ALGORITHM_LINK_DELIMITER) + n += 1u; + + return n; +} + + +/** + * Decompose the chain of hash algorithms specified in + * a password hash string + * + * @param hash The password hash string to decompose; may be modified; must not be `NULL` + * @param chain_out_array Output array for the hash algorithms; each element will be an offset of `hash` + * @param size The number of elements `chain_out_buffer` fit + * @return The number of chained hashes (same as `librecrypt_chain_length(hash)`) + * + * Algorithms are delimited using `LIBRECRYPT_ALGORITHM_LINK_DELIMITER` + * (which is `'>'`). The first `size - 1u` occurences will be set to `NULL`. + * `hash` can be restored by setting the terminating NUL byte in each but + * the last string, that is stored to `chain_out_array`, to + * `LIBRECRYPT_ALGORITHM_LINK_DELIMITER` + * + * If positive `size` is smaller than the returned value, + * `chain_out_array[size - 1u]` will contain all algorithms + * (still delimited by `LIBRECRYPT_ALGORITHM_LINK_DELIMITER`) + * that where not placed in earlier slots + * + * Unless already stripped out before input, + * `chain_out_array[size - 1u]` (provided that `size` is + * positive) will end with the resulting hash + * + * @seealso librecrypt_chain_length + * @seealso librecrypt_decompose_chain1 + * @seealso librecrypt_next_algorithm + * + * This function is MT-Safe and AS-Safe + */ +LIBRECRYPT_READ_WRITE_STR__(1) LIBRECRYPT_NONNULL_1__ LIBRECRYPT_WUR__ +inline size_t +librecrypt_decompose_chain(char *hash, char **chain_out_array, size_t size) +{ + size_t n = 1u; + + if (size) + chain_out_array[0u] = hash; + + for (; *hash; hash++) { + if (*hash == LIBRECRYPT_ALGORITHM_LINK_DELIMITER) { + if (n < size) { + *hash = '\0'; + chain_out_array[n] = &hash[1u]; + } + n += 1u; + } + } + + return n; +} + + +/** + * Replace each `LIBRECRYPT_ALGORITHM_LINK_DELIMITER` + * (which is `'>'`) in a password hash string with NUL + * bytes, effectively decomposing it to one string per + * hash algorithm, with the hashing result still attached + * to the last one if it as in the string + * + * @param hash The password hash string to decompose; may be modified; must not be `NULL` + * @return The number of chained hashes (same as `librecrypt_chain_length(hash)`) + * + * @seealso librecrypt_decompose_chain + * @seealso librecrypt_next_algorithm + * + * This function is MT-Safe and AS-Safe + */ +LIBRECRYPT_READ_WRITE_STR__(1) LIBRECRYPT_NONNULL__ LIBRECRYPT_WUR__ +inline size_t +librecrypt_decompose_chain1(char *hash) +{ + size_t n = 1u; + + for (; *hash; hash++) { + if (*hash == LIBRECRYPT_ALGORITHM_LINK_DELIMITER) { + *hash = '\0'; + n += 1u; + } + } + + return n; +} + + +/** + * This function is called repeatedly to get each + * hash algorithm (with parameters) that shall + * be chained together according to a provided + * hash string + * + * @param hash On the initial call, this argument shall point + * to the password hash string; on each call the + * function will update it to the current parsing + * state, meaning it will set `*hash` to point to + * an offset of `*hash` or point to `NULL`; + * additionally at each call, except on the final + * call, the string will be modified + * @return On the first call, this will be the first hash + * algorithm to use, and the second call it will + * be the second algorithm to use (if there is a + * second one), and so on. Once all algorithms have + * been extracted and returned, `NULL` is returned. + * + * Except once the function has returned `NULL`, overwriting + * the terminating NUL byte in the previous returned string + * with `LIBRECRYPT_ALGORITHM_LINK_DELIMITER` (which is `'>'`) + * will restore the password hash string to its original content + * + * Unless already stripped out before input, the last returned + * string will end with the resulting hash + * + * @seealso librecrypt_decompose_chain + * @seealso librecrypt_decompose_chain1 + * + * This function is MT-Safe and AS-Safe + */ +LIBRECRYPT_NONNULL__ LIBRECRYPT_WUR__ +inline char * +librecrypt_next_algorithm(char **hash) +{ + char *ret; + + if (!*hash) + return NULL; + + ret = *hash; + *hash = strchr(*hash, LIBRECRYPT_ALGORITHM_LINK_DELIMITER); + if (*hash) + *(*hash)++ = '\0'; + return ret; +} + + +/** + * Encode a hash result or salt in ASCII, from binary + * + * This is the inverse of `librecrypt_decode` + * + * @param out_buffer Output buffer for the ASCII representation + * @param size The number bytes the function may write to `out_buffer` + * @param binary The raw binary data to encode + * @param len The number of bytes in `binary` + * @param lut The encoding alphabet, consisting of 64 characters, + * repeated 4 times + * @param pad The padding character to use at the end; the NUL byte if none + * @return The number of bytes that would have been written to `out_buffer`, + * excluding the terminating NUL byte, if `size` was sufficiently large + * + * On successful completion, the N bytes is written to + * `out_buffer` where N is the lesser of `size` and 1 in + * excess of the return value (this count includes the + * terminating NUL byte) + * + * On successful completion, if `size` is positive, + * the output to `out_buffer` is NUL terminated even + * if this truncates the string + * + * @seealso librecrypt_get_encoding + * + * This function is MT-Safe and AS-Safe + */ +LIBRECRYPT_WRITE_MEM__(1, 2) LIBRECRYPT_READ_MEM__(3, 4) LIBRECRYPT_NONNULL_I__(5) LIBRECRYPT_WUR__ +size_t librecrypt_encode(char *out_buffer, size_t size, const void *binary, size_t len, + const char lut[restrict static 256], char pad); + + +/** + * Encode a hash result or salt in ASCII, from binary + * + * This is the inverse of `librecrypt_decode` + * + * @param out_buffer Output buffer for the raw binary data + * @param size The number bytes the function may write to `out_buffer` + * @param ascii The ASCII encoding of the data, the text to decode + * @param len The number of bytes in `ascii` + * @param lut 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 + * @return The number of bytes that would have been written to `out_buffer`, + * if `size` was sufficiently large, -1 on failure + * @throws EINVAL `ascii` uses an invalid encoding + * + * On successful completion, the N bytes is written to + * `out_buffer` where N is the lesser of `size` and the + * return value + * + * @seealso librecrypt_get_encoding + * + * This function is MT-Safe and AS-Safe + */ +LIBRECRYPT_WRITE_MEM__(1, 2) LIBRECRYPT_READ_MEM__(3, 4) LIBRECRYPT_NONNULL_I__(5) LIBRECRYPT_WUR__ +ssize_t librecrypt_decode(void *out_buffer, size_t size, const char *ascii, size_t len, + const unsigned char lut[restrict static 256], char pad, int strict_pad); + + +/** + * Get the ASCII (base-64) encoding used for the + * last algorithm in a chain + * + * @param settings The algorithm chain + * @param len The number of bytes in `settings` + * @param pad_out Output parameter for the padding character, + * will be set to the NUL byte if there is none + * @param strict_pad_out Set to 1 if encodings must be padded to a + * multiple of 4 bytes using `*pad_out` at the end, + * 0 otherwise; set arbitrarily if `*pad_out` is + * set to the NUL byte + * @param decoding 0 if the returned pointer should be useful for, + * `librecrypt_encode`, otherwise it will be useful + * for `librecrypt_decode` + * @return If `decoding` is 0: + * the encoding alphabet, consisting of 64 characters, + * repeated 4 times; + * otherwise: + * alphabet reverse lookup table, mapping any valid + * character (except the padding character) to the + * value of that character in the encoding alphabet, + * and any other character to the value `0xFF`; + * but `NULL` on failure + * @throws ENOSYS The last algorithm in `settings` is not recognised + * or was disabled at compile-time + * + * The return type is `const char *` if `decoding` is 0, + * and `const unsigned char *` otherwise + * + * `*pad_out` shall be used for the `pad` parameter of the `librecrypt_encode` + * and `librecrypt_decode` functions, however the NUL byte may also be used + * for the `librecrypt_encode` function if `*strict_pad_out` is set to 0 + * + * `*strict_pad_out` shall be used for the `strict_pad` parameter of the + * `librecrypt_decode` function + * + * The returned pointer (unless `NULL`) shall be used for the `lut` parameter + * of the `librecrypt_encode` function if `decoding` is 0, and for the `lut` + * parameter of the `librecrypt_decode` function otherwise + * + * This function is MT-Safe and AS-Safe + */ +LIBRECRYPT_READ_STR__(1) LIBRECRYPT_NONNULL__ LIBRECRYPT_WUR__ +const void *librecrypt_get_encoding(const char *settings, size_t len, char *pad_out, + int *strict_pad_out, int decoding); + + +/** + * Securely erase a memory buffer + * + * @param buffer The memory to erase + * @param size The number of bytes in `buffer` to erase + * + * @seealso librecrypt_wipe_str + * + * This function is MT-Safe and AS-Safe + */ +LIBRECRYPT_WRITE_MEM__(1, 2) +void librecrypt_wipe(void *buffer, size_t size); + + +/** + * Securely erase a string + * + * @param string The string to erase + * + * @seealso librecrypt_wipe + * + * This function is MT-Safe and AS-Safe + */ +LIBRECRYPT_READ_WRITE_STR__(1) +inline void +librecrypt_wipe_str(char *string) +{ + if (string) + librecrypt_wipe(string, strlen(string)); +} + + +/** + * Compare two memory segments in constant-time + * + * @param a One of the segments to compare + * @param b The other segments to compare + * @param len The number of bytes to compare + * @return 1 if the `a` and `b` are equal in their + * first `len` bytes, 0 otherwise + * + * This function is MT-Safe and AS-Safe + */ +LIBRECRYPT_READ_MEM__(1, 3) LIBRECRYPT_READ_MEM__(2, 3) LIBRECRYPT_WUR__ +int librecrypt_equal_binary(const void *a, const void *b, size_t len); + + +/** + * Compare two strings in constant-time + * + * If the strings are of different length, + * it will stop immediately after measurement; + * this should not happen in the real world as + * this function is used to compare password + * hashes, whose length depends strictly on the + * configuration, not the input password + * + * @param a One of the strings to compare + * @param b The other string to compare + * @return 1 if the `a` and `b` are equal, 0 otherwise + * + * This function is MT-Safe and AS-Safe + */ +LIBRECRYPT_READ_STR__(1) LIBRECRYPT_READ_STR__(2) LIBRECRYPT_WUR__ LIBRECRYPT_NONNULL__ +inline int +librecrypt_equal(const char *a, const char *b) +{ + size_t n = strlen(a); + size_t m = strlen(b); + return n == m && librecrypt_equal_binary(a, b, n); +} + + +/** + * Locate all asteriskes followed by a non-negative + * decimal number and replace each with ASCII-encded + * random bytes (as many bytes as the number specifies) + * + * @param out_buffer Output buffer for the ASCII representation + * @param size The number bytes the function may write to `out_buffer` + * @param settings The string to replace the asterisk-encoding in + * (the asterisk-free version is written to `out_buffer`) + * @param rng Random number generating function, `NULL` can be used + * for a default cryptographic random number generator; + * the function shall generate between 1 and `n` (inclusively) + * bytes and write them to the front of `out`, and return + * the number of generated bytes, or -1 on failure + * @param user Passed as the third argument to `*rng` for user-defined + * purposes, ignored if `rng` is `NULL` + * @return The number of bytes that would have been written to `out_buffer`, + * if `size` was sufficiently large, -1 on failure + * @throws ERANGE The expected return value is greater than {SSIZE_MAX} + * @throws EINVAL Asterisk-encoding was used in an invalid context + * @throws ENOSYS `settings` contain an algorithm that is not recognised + * or was disabled at compile-time + * + * If `rng` is `NULL`, any encountered `EINTR` is ignored, + * however, if it is encountered `errno` will be set to `EINTR`, + * but `errno` will remain unmodified otherwise, except if + * the function fails due to one of the above listed reasons + * + * Failure can only happen through `*rng`, it is + * responsible for setting `errno` if desired, or + * because of the conditions listed above` + * + * On successful completion, the N bytes is written to + * `out_buffer` where N is the lesser of `size` and 1 in + * excess of the return value (this count includes the + * terminating NUL byte) + * + * On successful completion, if `size` is positive, + * the output to `out_buffer` is NUL terminated even + * if this truncates the string + * + * On failure, `out_buffer` may be partially written + * + * The encoding depend on the algorithm, which is why + * it can fail with `EINVAL` or `ENOSYS` + * + * When `rng` is non-`NULL`, this function inherits any + * MT-Unsafe and AS-Unsafe properties from `*rng`, being + * is MT-Safe and AS-Safe as a baseline; however when + * `rng` is `NULL`, this function is MT-Safe but AS-Unsafe + */ +LIBRECRYPT_WRITE_MEM__(1, 2) LIBRECRYPT_READ_STR__(3) LIBRECRYPT_WUR__ +ssize_t librecrypt_realise_salts(char *restrict out_buffer, size_t size, const char *settings, + ssize_t (*rng)(void *out, size_t n, void *user), void *user); + + +/** + * Generate a password hash setting string + * + * @param out_buffer Output buffer for the ASCII representation + * @param size The number bytes the function may write to `out_buffer` + * @param algorithm Hash algorithm string; it need not specify any parameters + * however any unspecified parameter that cannot be determined + * by `memcost` or `timecost` will be set to an arbitrary, + * valid value. If `NULL`, a default, strong algorithm is + * selected. Note that the empty string represents the + * deprecated DES algorithm. Algorithm chains are disallowed. + * @param memcost Approximate memory cost in bytes, ignored if not supported; + * a default value is selected if 0 is specified + * @param timecost Total time cost, in an arbitrary, algorithm-dependent, + * implementation-dependent, monotonic (may have flat sections) + * unit and scale that is approximately linear; a default value + * is selected if 0 is specified + * @param gensalt Zero if salts shall specified to be randomly generated + * (reusable password hash setting string), + * non-zero if random salts shall be included in the generated + * output (non-reusable password hash setting string) + * @param rng Random number generating function, `NULL` can be used + * for a default cryptographic random number generator; + * the function shall generate between 1 and `n` (inclusively) + * bytes and write them to the front of `out`, and return + * the number of generated bytes, or -1 on failure; + * ignored if `gensalt` is zero + * @param user Passed as the third argument to `*rng` for user-defined + * purposes, ignored if `rng` is `NULL` or if `gensalt` is zero + * @return The number of bytes that would have been written to + * `out_buffer`, if `size` was sufficiently large, -1 on failure + * @throws EINVAL `algorithm` represents a chain of algorithms + * @throws ENOSYS `algorithm` represents an algorithm that is not + * recognised or was disabled at compile-time + * @throws ENOSYS `algorithm` is `NULL` but all algorithms were disabled at + * compile-time + * + * If `rng` is `NULL`, any encountered `EINTR` is ignored, + * however, if it is encountered `errno` will be set to `EINTR`, + * but `errno` will remain unmodified otherwise, except if + * the function fails due to `EINVAL` or `ENOSYS` + * + * Failure can only happen through `*rng`, it is + * responsible for setting `errno` if desired, or + * because of the conditions for `EINVAL` or `ENOSYS` + * + * On successful completion, the N bytes is written to + * `out_buffer` where N is the lesser of `size` and 1 in + * excess of the return value (this count includes the + * terminating NUL byte) + * + * On successful completion, if `size` is positive, + * the output to `out_buffer` is NUL terminated even + * if this truncates the string + * + * On failure, `out_buffer` may be partially written + * + * This function is MT-Safe but AS-Unsafe + */ +LIBRECRYPT_WRITE_MEM__(1, 2) LIBRECRYPT_READ_STR__(3) LIBRECRYPT_WUR__ +ssize_t librecrypt_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); + + +/** + * Compute the hash of a password + * + * The hash will be stored in raw binary format without any settings + * + * @param out_buffer Output buffer for the hash result + * @param size The number bytes the function may write to `out_buffer` + * @param phrase The password to hash, may contain NUL bytes + * @param len The number of bytes in `phrase` + * @param settings The password hash configuration string, + * may contain resulting hash, which will be ignored + * @param reserved Reserved for future use, should be `NULL` + * @return The number of bytes that would have been written to `out_buffer` + * if `size` was sufficiently large; -1 on failure + * + * @throws EINVAL `reserved` is non-`NULL` (this case will be removed + * once `reserved` as being used by the library) + * @throws EINVAL `settings` is invalid (invalid algorithm configuration, + * invalid configuration syntax, or the output from one + * chained hash algorithm cannot be input the next algorithm + * in the chain (either because of format or length issues)) + * @throws EINVAL `settings` uses asterisk-encoding to specify random salts + * @throws ERANGE `len` is too large or too small for the the selected + * initial algorithm in the algorithm chain + * @throws ENOMEM Failed to allocate internal scratch memory + * @throws ENOSYS A selected hash algorithm is either not recognised + * disabled at compile-time + * + * Any encountered `EINTR` is ignored + * + * On successful completion, the N bytes is written to + * `out_buffer` where N is the lesser of `size` and the + * return value + * + * On failure, `out_buffer` will remain unmodified + * + * @seealso librecrypt_hash + * @seealso librecrypt_crypt + * @seealso librecrypt_test_supported + * + * This function is MT-Safe but AS-Unsafe + */ +LIBRECRYPT_WRITE_MEM__(1, 2) LIBRECRYPT_READ_MEM__(3, 4) LIBRECRYPT_READ_STR__(5) +LIBRECRYPT_NONNULL_I__(5) LIBRECRYPT_WUR__ +ssize_t librecrypt_hash_binary(char *restrict out_buffer, size_t size, const char *phrase, size_t len, + const char *settings, void *reserved); + + +/** + * Compute the hash of a password + * + * The hash will be encoded with `librecrypt_encode` + * but stored without any settings + * + * @param out_buffer Output buffer for the hash result + * @param size The number bytes the function may write to `out_buffer` + * @param phrase The password to hash, may contain NUL bytes + * @param len The number of bytes in `phrase` + * @param settings The password hash configuration string, + * may contain resulting hash, which will be ignored + * @param reserved Reserved for future use, should be `NULL` + * @return The number of bytes that would have been written to `out_buffer` + * if `size` was sufficiently large, excluding a terminating + * NUL byte; -1 on failure + * + * @throws EINVAL `reserved` is non-`NULL` (this case will be removed + * once `reserved` as being used by the library) + * @throws EINVAL `settings` is invalid (invalid algorithm configuration, + * invalid configuration syntax, or the output from one + * chained hash algorithm cannot be input the next algorithm + * in the chain (either because of format or length issues)) + * @throws EINVAL `settings` uses asterisk-encoding to specify random salts + * @throws ERANGE `len` is too large or too small for the the selected + * initial algorithm in the algorithm chain + * @throws ENOMEM Failed to allocate internal scratch memory + * @throws ENOSYS A selected hash algorithm is either not recognised + * disabled at compile-time + * + * Any encountered `EINTR` is ignored + * + * On successful completion, the N bytes is written to + * `out_buffer` where N is the lesser of `size` and 1 in + * excess of the return value (this count includes the + * terminating NUL byte) + * + * On successful completion, if `size` is positive, + * the output to `out_buffer` is NUL terminated even + * if this truncates the string + * + * On failure, `out_buffer` will remain unmodified + * + * @seealso librecrypt_hash_binary + * @seealso librecrypt_crypt + * @seealso librecrypt_test_supported + * + * This function is MT-Safe but AS-Unsafe + */ +LIBRECRYPT_WRITE_MEM__(1, 2) LIBRECRYPT_READ_MEM__(3, 4) LIBRECRYPT_READ_STR__(5) +LIBRECRYPT_NONNULL_I__(5) LIBRECRYPT_WUR__ +ssize_t librecrypt_hash(char *restrict out_buffer, size_t size, const char *phrase, size_t len, + const char *settings, void *reserved); + + +/** + * Compute the hash of a password + * + * The hash will be encoded with `librecrypt_encode`; + * and the settings will be included in the front + * + * @param out_buffer Output buffer for the hash result + * @param size The number bytes the function may write to `out_buffer` + * @param phrase The password to hash, may contain NUL bytes + * @param len The number of bytes in `phrase` + * @param settings The password hash configuration string, + * may contain resulting hash, which will be ignored + * @param reserved Reserved for future use, should be `NULL` + * @return The number of bytes that would have been written to `out_buffer` + * if `size` was sufficiently large, excluding a terminating + * NUL byte; -1 on failure + * + * @throws EINVAL `reserved` is non-`NULL` (this case will be removed + * once `reserved` as being used by the library) + * @throws EINVAL `settings` is invalid (invalid algorithm configuration, + * invalid configuration syntax, or the output from one + * chained hash algorithm cannot be input the next algorithm + * in the chain (either because of format or length issues)) + * @throws ERANGE `len` is too large or too small for the the selected + * initial algorithm in the algorithm chain + * @throws ENOMEM Failed to allocate internal scratch memory + * @throws ENOSYS A selected hash algorithm is either not recognised + * disabled at compile-time + * + * Any encountered `EINTR` is ignored + * + * On successful completion, the N bytes is written to + * `out_buffer` where N is the lesser of `size` and 1 in + * excess of the return value (this count includes the + * terminating NUL byte) + * + * On successful completion, if `size` is positive, + * the output to `out_buffer` is NUL terminated even + * if this truncates the string + * + * On failure, `out_buffer` may be partially written + * + * @seealso librecrypt_hash_binary + * @seealso librecrypt_hash + * @seealso librecrypt_test_supported + * + * This function is MT-Safe but AS-Unsafe + */ +LIBRECRYPT_WRITE_MEM__(1, 2) LIBRECRYPT_READ_MEM__(3, 4) LIBRECRYPT_READ_STR__(5) +LIBRECRYPT_NONNULL_I__(5) LIBRECRYPT_WUR__ +ssize_t librecrypt_crypt(char *restrict out_buffer, size_t size, const char *phrase, size_t len, + const char *settings, void *reserved); + + +/** + * Check whether a hash algorithm chain is supported, + * for the given input, and that each algorithm + * configuration is valid + * + * @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 + * @return 1 if the configuration is supported, 0 otherwise, which + * means part of the string is invalid: the algorithm does + * not exist, supported was disabled at compile-time, or an + * algorithm it does not support the output of the previous + * algorithm in the chain as input; additionally a small + * number of legacy algorithms require the passphrase to + * be within a specific length range or be valid text, + * if the selected word does not match such constraints + * for the first algorithm in the chain, 0 is returned + * + * This function is MT-Safe and AS-Safe + */ +LIBRECRYPT_READ_STR__(4) LIBRECRYPT_NONNULL_I__(4) LIBRECRYPT_WUR__ +int librecrypt_test_supported(const char *phrase, size_t len, int text, const char *settings); + + +/** + * Chain togather another set of hash algorithms + * + * @param out_buffer Output buffer for the new password hash string + * @param size The number bytes the function may write to `out_buffer` + * @param augend THe existing password hash string; if it contains a + * hash result, a new hash result will be computed and + * any random number generation specification in `augment` + * will be realised + * @param augment Password hash setting string describing the additional + * hashing to perform; if it contains a hash result, that + * part will be ignored + * @param reserved Reserved for future use, should be `NULL` + * @return The number of bytes that would have been written to `out_buffer` + * if `size` was sufficiently large, excluding a terminating + * NUL byte; -1 on failure + * + * @throws EINVAL `reserved` is non-`NULL` (this case will be removed + * once `reserved` as being used by the library) + * @throws EINVAL `settings` is invalid (invalid algorithm configuration, + * invalid configuration syntax, or the output from one + * chained hash algorithm cannot be input the next algorithm + * in the chain (either because of format or length issues)) + * @throws ERANGE `len` is too large or too small for the the selected + * initial algorithm in the algorithm chain + * @throws ENOMEM Failed to allocate internal scratch memory + * @throws ENOSYS A selected hash algorithm is either not recognised + * disabled at compile-time + * + * On successful completion, the N bytes is written to + * `out_buffer` where N is the lesser of `size` and 1 in + * excess of the return value (this count includes the + * terminating NUL byte) + * + * On successful completion, if `size` is positive, + * the output to `out_buffer` is NUL terminated even + * if this truncates the string + * + * On failure, `out_buffer` will remain unmodified + * + * This function is MT-Safe but AS-Unsafe + */ +LIBRECRYPT_WRITE_MEM__(1, 2) LIBRECRYPT_READ_WRITE_STR__(3) LIBRECRYPT_READ_STR__(4) +LIBRECRYPT_NONNULL_I__(3) LIBRECRYPT_NONNULL_I__(4) LIBRECRYPT_WUR__ +ssize_t librecrypt_add_algorithm(char *out_buffer, size_t size, char *augend, + const char *restrict augment, void *reserved); + + +#endif diff --git a/librecrypt_add_algorithm.3 b/librecrypt_add_algorithm.3 new file mode 100644 index 0000000..77867e8 --- /dev/null +++ b/librecrypt_add_algorithm.3 @@ -0,0 +1,134 @@ +.TH LIBRECRYPT_ADD_ALGORITHM 3 LIBRECRYPT +.SH NAME +librecrypt_add_algorithm - Append an algorithm chain to a password hash string + +.SH SYNOPSIS +.nf +#include <librecrypt.h> + +ssize_t \fBlibrecrypt_add_algorithm\fP(char *restrict \fIout_buffer\fP, size_t \fIsize\fP, + char *\fIaugend\fP, const char *restrict \fIaugment\fP, + void *\fIreserved\fP); +.fi +.PP +Link with +.IR -lrecrypt . +Static linking may require additional flags +depending on enabled hash algorithms. + +.SH DESCRIPTION +The +.BR librecrypt_add_algorithm () +function chains together another set of hash algorithms. +.PP +The +.I augend +argument is the existing password hash string. +If it already contains a hash result, +a new hash result will be computed and +any random number generation specification in +.I augment +will be realised. +.PP +The +.I augment +argument is a password hash setting string describing +the additional hashing to perform. +If it contains a hash result, that part is ignored. +.PP +The +.I reserved +parameter is reserved for future use and should be +.IR NULL . +.PP +On successful completion, if +.I size +is positive, the output is null byte-terminated even +if truncated. +.PP +On failure, +.I out_buffer +remains unmodified. +.PP +The +.I augend +and +.I augment +parameters must not be +.IR NULL . +The +.I out_buffer +parameter may only be +.I NULL +if if +.I size +is 0. + +.SH RETURN VALUES +The +.BR librecrypt_add_algorithm () +function returns the number of bytes that would have +been written to +.IR out_buffer +if +.I size +was sufficiently large, excluding the terminating +null byte. +On failure, -1 is returned and +.I errno +is set to describe the error. + +.SH ERRORS +The +.BR librecrypt_add_algorithm () +function will fail if: +.TP +.B EINVAL +.I reserved +is not +.IR NULL . +.TP +.B EINVAL +.I augend +or +.I augment +is invalid. +.TP +.B ERANGE +The selected initial algorithm requires a different password length. +.TP +.B ENOMEM +Failed to allocate internal scratch memory. +.TP +.B ENOSYS +A selected hash algorithm is not recognised or was disabled at compile-time. + +.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_add_algorithm () +T} Thread safety MT-Safe +T{ +.BR librecrypt_add_algorithm () +T} Async-signal safety AS-Unsafe +.TE +.sp + +.SH HISTORY +The +.BR librecrypt_add_algorithm () +function was introduced in version 1.0 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_crypt (3), +.BR librecrypt_make_settings (3), +.BR librecrypt_realise_salts (3) diff --git a/librecrypt_add_algorithm.c b/librecrypt_add_algorithm.c new file mode 100644 index 0000000..a2d8aa4 --- /dev/null +++ b/librecrypt_add_algorithm.c @@ -0,0 +1,96 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +ssize_t +librecrypt_add_algorithm(char *out_buffer, size_t size, char *augend, const char *restrict augment, void *reserved) +{ + size_t prefix1 = librecrypt_settings_prefix(augend); + size_t prefix2, min, ret, len, phraselen; + char *phrase, pad; + int strict_pad; + const unsigned char *lut; + ssize_t r; + +#define COPY_PREFIX()\ + do {\ + size -= 1u;\ + min = prefix1 < size ? prefix1 : size;\ + if (out_buffer != augend)\ + memmove(out_buffer, augend, min);\ + out_buffer = &out_buffer[min];\ + size -= min;\ + if (size) {\ + *out_buffer++ = LIBRECRYPT_ALGORITHM_LINK_DELIMITER;\ + size -= 1u;\ + }\ + } while (0) + + if (!augend[prefix1]) { + prefix2 = librecrypt_settings_prefix(augment); + ret = prefix1 + 1u + prefix2; + if (size) { + COPY_PREFIX(); + min = prefix2 < size ? prefix2 : size; + memcpy(out_buffer, augment, min); + out_buffer[min] = '\0'; + } + return (ssize_t)ret; + } + + if (size <= prefix1 + 2u) { + phrase = NULL; + phraselen = 0u; + } else { + lut = librecrypt_get_encoding(augend, strlen(augend), &pad, &strict_pad, 1); + if (!lut) + return -1; + + len = strlen(&augend[prefix1]); + r = librecrypt_decode(NULL, 0, &augend[prefix1], len, lut, pad, strict_pad); + if (r <= 0) { + if (!r) + abort(); + return -1; + } + phraselen = (size_t)r; + phrase = malloc(phraselen); + if (!phrase) + return -1; + if (librecrypt_decode(phrase, phraselen, &augend[prefix1], len, lut, pad, strict_pad) != r) + abort(); + } + + COPY_PREFIX(); + ret = prefix1 + 1u; + r = librecrypt_crypt(out_buffer, size + 1u, phrase, phraselen, augment, reserved); + if (r <= 0) { + librecrypt_wipe(phrase, phraselen); + free(phrase); + if (!r) + abort(); + return -1; + } + ret += (size_t)r; + + librecrypt_wipe(phrase, phraselen); + free(phrase); + return (ssize_t)ret; +} + + +#else + + +int +main(void) +{ + SET_UP_ALARM(); + + return 0; +} + + +#endif +/* TODO test */ diff --git a/librecrypt_algorithms_.c b/librecrypt_algorithms_.c new file mode 100644 index 0000000..34c6b3f --- /dev/null +++ b/librecrypt_algorithms_.c @@ -0,0 +1,69 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +#define ENTRY(ALGO)\ + {\ + .get_prefix = &librecrypt__##ALGO##__get_prefix,\ + .is_algorithm = &librecrypt__##ALGO##__is_algorithm,\ + .hash = librecrypt__##ALGO##__hash,\ + .test_supported = &librecrypt__##ALGO##__test_supported,\ + .make_settings = &librecrypt__##ALGO##__make_settings,\ + .encoding_lut = &librecrypt__##ALGO##__encoding_lut,\ + .decoding_lut = &librecrypt__##ALGO##__decoding_lut,\ + .hash_size = ALGO##__HASH_SIZE,\ + .strict_pad = ALGO##__STRICT_PAD,\ + .pad = ALGO##__PAD\ + } + + +#define X(ALGO) IF__##ALGO##__SUPPORTED(ENTRY(ALGO) COMMA) +struct algorithm librecrypt_algorithms_[] = { + LIST_ALGORITHMS(X) + END_OF_ALGORITHMS +}; +#undef X + + +#else + + +int +main(void) +{ +#define X(ALGO) IF__##ALGO##__SUPPORTED(+ 1u) + const size_t count = 0u LIST_ALGORITHMS(X); +#undef X + size_t i; + + SET_UP_ALARM(); + + for (i = 0u; i < count; i++) { + CHECK(!IS_END_OF_ALGORITHMS(&librecrypt_algorithms_[i])); + CHECK(librecrypt_algorithms_[i].get_prefix != NULL); + CHECK(librecrypt_algorithms_[i].is_algorithm != NULL); + CHECK(librecrypt_algorithms_[i].hash != NULL); + CHECK(librecrypt_algorithms_[i].test_supported != NULL); + CHECK(librecrypt_algorithms_[i].make_settings != NULL); + CHECK(librecrypt_algorithms_[i].encoding_lut != NULL); + CHECK(librecrypt_algorithms_[i].decoding_lut != NULL); + CHECK(librecrypt_algorithms_[i].hash_size > 0u); + CHECK(librecrypt_algorithms_[i].strict_pad >= 0); + CHECK(librecrypt_algorithms_[i].strict_pad <= 1); + if (librecrypt_algorithms_[i].pad) { + CHECK(librecrypt_algorithms_[i].pad > ' '); + CHECK(librecrypt_algorithms_[i].pad < 0x7F); + CHECK(librecrypt_algorithms_[i].pad != LIBRECRYPT_HASH_COMPOSITION_DELIMITER); + CHECK(librecrypt_algorithms_[i].pad != LIBRECRYPT_ALGORITHM_LINK_DELIMITER); + CHECK(librecrypt_algorithms_[i].pad != '*'); + } + } + assert(i == count); + CHECK(IS_END_OF_ALGORITHMS(&librecrypt_algorithms_[count])); + + return 0; +} + + +#endif diff --git a/librecrypt_chain_length.3 b/librecrypt_chain_length.3 new file mode 100644 index 0000000..eb0b1cc --- /dev/null +++ b/librecrypt_chain_length.3 @@ -0,0 +1,69 @@ +.TH LIBRECRYPT_CHAIN_LENGTH 3 LIBRECRYPT +.SH NAME +librecrypt_chain_length - Get number of algorithms in a chained password hash string + +.SH SYNOPSIS +.nf +#include <librecrypt.h> + +size_t \fBlibrecrypt_chain_length\fP(const char *\fIhash\fP); +.fi +.PP +Link with +.IR -lrecrypt . + +.SH DESCRIPTION +The +.BR librecrypt_chain_length () +function returns the number of hash algorithms +specified in the chain contained in +.IR hash . +Algorithms are delimited by +.I LIBRECRYPT_ALGORITHM_LINK_DELIMITER +(which is +.BR "\(aq>\(aq" ). +.PP +.I hash +must not be +.IR NULL . + +.SH RETURN VALUES +The +.BR librecrypt_chain_length () +function returns the number of chained hash algorithms. +The return value is always non-zero. + +.SH ERRORS +The +.BR librecrypt_chain_length () +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_chain_length () +T} Thread safety MT-Safe +T{ +.BR librecrypt_chain_length () +T} Async-signal safety AS-Safe +.TE +.sp + +.SH HISTORY +The +.BR librecrypt_chain_length () +function was introduced in version 1.0 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_decompose_chain (3), +.BR librecrypt_decompose_chain1 (3), +.BR librecrypt_next_algorithm (3) diff --git a/librecrypt_chain_length.c b/librecrypt_chain_length.c new file mode 100644 index 0000000..64b9472 --- /dev/null +++ b/librecrypt_chain_length.c @@ -0,0 +1,27 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +extern inline size_t librecrypt_chain_length(const char *hash); + + +#else + + +int +main(void) +{ + SET_UP_ALARM(); + EXPECT(librecrypt_chain_length("") == 1u); + EXPECT(librecrypt_chain_length("a$b") == 1u); + EXPECT(librecrypt_chain_length(">") == 2u); + EXPECT(librecrypt_chain_length(">>") == 3u); + EXPECT(librecrypt_chain_length("a$b>c$d>e$f") == 3u); + EXPECT(librecrypt_chain_length("a$b>c$d>e$f>") == 4u); + EXPECT(librecrypt_chain_length(">a$b>c$d>e$f>") == 5u); + return 0; +} + + +#endif diff --git a/librecrypt_crypt.3 b/librecrypt_crypt.3 new file mode 100644 index 0000000..0237862 --- /dev/null +++ b/librecrypt_crypt.3 @@ -0,0 +1,131 @@ +.TH LIBRECRYPT_CRYPT 3 LIBRECRYPT +.SH NAME +librecrypt_crypt - Compute password hash encoded in ASCII with settings prefix + +.SH SYNOPSIS +.nf +#include <librecrypt.h> + +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, void *\fIreserved\fP); +.fi +.PP +Link with +.IR -lrecrypt . +Static linking may require additional flags +depending on enabled hash algorithms. + +.SH DESCRIPTION +The +.BR librecrypt_crypt () +function computes the hash of a password. +The resulting hash is encoded with +.BR librecrypt_encode (3) +and the settings string is included in the front +of the output. +.PP +The password is provided in +.IR phrase +and can contain null bytes; its length is +specified, in bytes, by +.IR len . +.PP +The password hash configuration string is +provided in +.IR settings . +If +.I settings +contains a resulting hash, it is ignored. +.PP +The +.I reserved +parameter is reserved for future use and +should be +.IR NULL . +.PP +On successful completion, if +.I size +is positive, the output is +null byte-terminated even if truncated. +.PP +Any encountered +.BR EINTR +is ignored. +.PP +On failure, +.I out_buffer +may be partially written. +.PP +.I settings +must not be +.IR NULL . + +.SH RETURN VALUES +The +.BR librecrypt_crypt () +function returns the number of bytes that would +have been written to +.IR out_buffer +if +.I size +was sufficiently large, excluding the terminating +null byte. On failure, -1 is returned and +.I errno +is set to describe the error. + +.SH ERRORS +The +.BR librecrypt_crypt () +function will fail if: +.TP +.B EINVAL +.I reserved +is not +.IR NULL . +.TP +.B EINVAL +.I settings +is invalid. +.TP +.B ERANGE +.I len +is too large or too small for the selected +initial algorithm. +.TP +.B ENOMEM +Failed to allocate internal scratch memory. +.TP +.B ENOSYS +A selected hash algorithm is not recognised +or was disabled at compile-time. + +.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_crypt () +T} Thread safety MT-Safe +T{ +.BR librecrypt_crypt () +T} Async-signal safety AS-Unsafe +.TE +.sp + +.SH HISTORY +The +.BR librecrypt_crypt () +function was introduced in version 1.0 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_hash_binary (3), +.BR librecrypt_hash (3), +.BR librecrypt_test_supported (3) diff --git a/librecrypt_crypt.c b/librecrypt_crypt.c new file mode 100644 index 0000000..6192650 --- /dev/null +++ b/librecrypt_crypt.c @@ -0,0 +1,26 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +ssize_t +librecrypt_crypt(char *restrict out_buffer, size_t size, const char *phrase, size_t len, const char *settings, void *reserved) +{ + return librecrypt_hash_(out_buffer, size, phrase, len, settings, reserved, ASCII_CRYPT); +} + + +#else + + +int +main(void) +{ + SET_UP_ALARM(); + + return 0; +} + + +#endif +/* TODO test */ diff --git a/librecrypt_decode.3 b/librecrypt_decode.3 new file mode 100644 index 0000000..cab9aad --- /dev/null +++ b/librecrypt_decode.3 @@ -0,0 +1,104 @@ +.TH LIBRECRYPT_DECODE 3 LIBRECRYPT +.SH NAME +librecrypt_decode - Decode ASCII encoding of a salt or hash result into binary + +.SH SYNOPSIS +.nf +#include <librecrypt.h> + +ssize_t \fBlibrecrypt_decode\fP(void *\fIout_buffer\fP, size_t \fIsize\fP, + const char *\fIascii\fP, size_t \fIlen\fP, + const unsigned char \fIlut\fP[restrict static 256], + char \fIpad\fP, int \fIstrict_pad\fP); +.fi +.PP +Link with +.IR -lrecrypt . + +.SH DESCRIPTION +The +.BR librecrypt_decode () +function decodes +.I len +bytes of ASCII text from +.IR ascii +into raw binary data written to +.IR out_buffer . +This is used for decoding hash results and salts. +.PP +The +.I lut +argument is a reverse lookup table that maps any +valid character (except the padding character) to +its value in the encoding alphabet and maps any +invalid character to +.BR 0xFFu . +.PP +The +.I pad +argument specifies the padding character to use +at the end, or the null byte if none. +.PP +If +.I strict_pad +is non-zero, the padding at the end is mandatory. +.PP +On successful completion, up to +.I size +bytes are written to +.IR out_buffer . +The return value is the number of bytes that would +have been written if +.I size +was sufficiently large. + +.SH RETURN VALUES +The +.BR librecrypt_decode () +function returns the number of bytes that would +have been written to +.IR out_buffer +if +.I size +was sufficiently large. +On failure, -1 is returned and +.IR errno +is set to describe the error. + +.SH ERRORS +The +.BR librecrypt_decode () +function will fail if: +.TP +.B EINVAL +.I ascii +uses an invalid encoding. + +.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_decode () +T} Thread safety MT-Safe +T{ +.BR librecrypt_decode () +T} Async-signal safety AS-Safe +.TE +.sp + +.SH HISTORY +The +.BR librecrypt_decode () +function was introduced in version 1.0 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_encode (3), +.BR librecrypt_get_encoding (3) diff --git a/librecrypt_decode.c b/librecrypt_decode.c new file mode 100644 index 0000000..ee451a7 --- /dev/null +++ b/librecrypt_decode.c @@ -0,0 +1,257 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +ssize_t +librecrypt_decode(void *out_buffer, size_t size, const char *ascii, size_t len, + const unsigned char lut[restrict static 256], char pad, int strict_pad) +{ + const unsigned char *str = (const unsigned char *)ascii; + unsigned char *data = out_buffer; + unsigned char a, b, c, d; + size_t n = 0u; + + for(; len >= 4u; str += 4u) { + len -= 4u; + + a = lut[str[0u]]; + b = lut[str[1u]]; + if ((a | b) == 0xFFu) + goto einval; + if (n < size) + data[n] = (unsigned char)((a << 2) | (b >> 4)); + n++; + + c = lut[str[2u]]; + if (c == 0xFFu) { + if (len || !pad || str[2u] != pad || str[3u] != pad) + goto einval; + break; + } + if (n < size) + data[n] = (unsigned char)((b << 4u) | (c >> 2u)); + n++; + + d = lut[str[3u]]; + if (d == 0xFFu) { + if (len || !pad || str[3u] != pad) + goto einval; + break; + } + if (n < size) + data[n] = (unsigned char)((c << 6) | (d >> 0)); + n++; + } + + if (len && strict_pad && pad) + goto einval; + + switch (len) { + case 0u: + goto out; + case 1u: + goto einval; + + default: + a = lut[str[0u]]; + b = lut[str[1u]]; + if ((a | b) == 0xFFu) + goto einval; + if (n < size) + data[n] = (unsigned char)((a << 2) | (b >> 4)); + n++; + if (len == 2u) + break; + + c = lut[str[2u]]; + if (c == 0xFFu) { + if (!pad || str[2u] != pad) + goto einval; + break; + } + if (n < size) + data[n] = (unsigned char)((b << 4u) | (c >> 2u)); + n++; + break; + } + +out: + return (ssize_t)n; + +einval: + errno = EINVAL; + return -1; +} + + +#else + + +static const unsigned char lut[256u] = { + XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, + XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, + XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, 62, XX, XX, XX, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, XX, XX, XX, XX, XX, XX, + XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, XX, XX, XX, XX, XX, + XX, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, XX, XX, XX, XX, XX, + XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, + XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, + XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, + XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, + XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, + XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, + XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, + XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX +}; + + +#define CHECK(BINARY, ASCII, PAD)\ + check((BINARY), sizeof(BINARY) - 1u, (ASCII PAD), sizeof(ASCII) - 1u, sizeof(ASCII PAD) - 1u) + + +static void +check(const char *binary, size_t binary_len, const char *ascii, size_t unpadded_len, size_t padded_len) +{ + char buf[16u]; + size_t i, j; + + assert(binary_len <= sizeof(buf)); + + assert(padded_len % 4u == 0u); + assert(padded_len / 4u == (unpadded_len + 3u) / 4u); + assert(padded_len >= unpadded_len); + + EXPECT(librecrypt_decode(NULL, 0u, ascii, unpadded_len, lut, '\0', 0) == (ssize_t)binary_len); + EXPECT(librecrypt_decode(NULL, 0u, ascii, unpadded_len, lut, '\0', 1) == (ssize_t)binary_len); + EXPECT(librecrypt_decode(NULL, 0u, ascii, unpadded_len, lut, '=', 0) == (ssize_t)binary_len); + + if (padded_len == unpadded_len) { + EXPECT(librecrypt_decode(NULL, 0u, ascii, unpadded_len, lut, '=', 1) == (ssize_t)binary_len); + } else { + errno = 0; + EXPECT(librecrypt_decode(NULL, 0u, ascii, unpadded_len, lut, '=', 1) == -1); + EXPECT(errno == EINVAL); + + errno = 0; + EXPECT(librecrypt_decode(NULL, 0u, ascii, padded_len, lut, '\0', 0) == -1); + EXPECT(errno == EINVAL); + + errno = 0; + EXPECT(librecrypt_decode(NULL, 0u, ascii, padded_len, lut, '\0', 1) == -1); + EXPECT(errno == EINVAL); + } + + EXPECT(librecrypt_decode(NULL, 0u, ascii, padded_len, lut, '=', 0) == (ssize_t)binary_len); + EXPECT(librecrypt_decode(NULL, 0u, ascii, padded_len, lut, '=', 1) == (ssize_t)binary_len); + + for (i = 0u; i < sizeof(buf); i++) { + memset(buf, 99, sizeof(buf)); + EXPECT(librecrypt_decode(buf, i, ascii, unpadded_len, lut, '\0', 0) == (ssize_t)binary_len); + j = i < binary_len ? i : binary_len; + EXPECT(!memcmp(buf, binary, j)); + for (; j < sizeof(buf); j++) + EXPECT(buf[j] == 99); + + memset(buf, 99, sizeof(buf)); + EXPECT(librecrypt_decode(buf, i, ascii, unpadded_len, lut, '\0', 1) == (ssize_t)binary_len); + j = i < binary_len ? i : binary_len; + EXPECT(!memcmp(buf, binary, j)); + for (; j < sizeof(buf); j++) + EXPECT(buf[j] == 99); + + memset(buf, 99, sizeof(buf)); + EXPECT(librecrypt_decode(buf, i, ascii, unpadded_len, lut, '=', 0) == (ssize_t)binary_len); + j = i < binary_len ? i : binary_len; + EXPECT(!memcmp(buf, binary, j)); + for (; j < sizeof(buf); j++) + EXPECT(buf[j] == 99); + + if (padded_len == unpadded_len) { + memset(buf, 99, sizeof(buf)); + EXPECT(librecrypt_decode(buf, i, ascii, unpadded_len, lut, '=', 1) == (ssize_t)binary_len); + j = i < binary_len ? i : binary_len; + EXPECT(!memcmp(buf, binary, j)); + for (; j < sizeof(buf); j++) + EXPECT(buf[j] == 99); + } else { + errno = 0; + EXPECT(librecrypt_decode(buf, i, ascii, unpadded_len, lut, '=', 1) == -1); + EXPECT(errno == EINVAL); + + errno = 0; + EXPECT(librecrypt_decode(buf, i, ascii, padded_len, lut, '\0', 0) == -1); + EXPECT(errno == EINVAL); + + errno = 0; + EXPECT(librecrypt_decode(buf, i, ascii, padded_len, lut, '\0', 1) == -1); + EXPECT(errno == EINVAL); + } + + memset(buf, 99, sizeof(buf)); + EXPECT(librecrypt_decode(buf, i, ascii, padded_len, lut, '=', 0) == (ssize_t)binary_len); + j = i < binary_len ? i : binary_len; + EXPECT(!memcmp(buf, binary, j)); + for (; j < sizeof(buf); j++) + EXPECT(buf[j] == 99); + + memset(buf, 99, sizeof(buf)); + EXPECT(librecrypt_decode(buf, i, ascii, padded_len, lut, '=', 1) == (ssize_t)binary_len); + j = i < binary_len ? i : binary_len; + EXPECT(!memcmp(buf, binary, j)); + for (; j < sizeof(buf); j++) + EXPECT(buf[j] == 99); + + } +} + + +int +main(void) +{ + int i; + + SET_UP_ALARM(); + + CHECK("", "", ""); + CHECK("\x00", "AA", "=="); + CHECK("\x00\x00", "AAA", "="); + CHECK("\x00\x00\x00", "AAAA", ""); + CHECK("12345678", "MTIzNDU2Nzg", "="); + CHECK("testtest", "dGVzdHRlc3Q", "="); + CHECK("zy[]y21 !", "enlbXXkyMSAh", ""); + CHECK("{~|~}~~~\x7f\x7f", "e358fn1+fn5/fw", "=="); + + for (i = 0; i <= 1; i++) { + errno = 0; + EXPECT(librecrypt_decode(NULL, 0u, "AA=*", 4u, lut, '=', i) == -1); + EXPECT(errno == EINVAL); + + errno = 0; + EXPECT(librecrypt_decode(NULL, 0u, "AA*=", 4u, lut, '=', i) == -1); + EXPECT(errno == EINVAL); + + errno = 0; + EXPECT(librecrypt_decode(NULL, 0u, "AA**", 4u, lut, '=', i) == -1); + EXPECT(errno == EINVAL); + + errno = 0; + EXPECT(librecrypt_decode(NULL, 0u, "A===", 4u, lut, '=', i) == -1); + EXPECT(errno == EINVAL); + + errno = 0; + EXPECT(librecrypt_decode(NULL, 0u, "A", 1u, lut, '=', i) == -1); + EXPECT(errno == EINVAL); + + errno = 0; + EXPECT(librecrypt_decode(NULL, 0u, "====", 4u, lut, '=', i) == -1); + EXPECT(errno == EINVAL); + } + + return 0; +} + + +#endif diff --git a/librecrypt_decompose_chain.3 b/librecrypt_decompose_chain.3 new file mode 100644 index 0000000..fc9f928 --- /dev/null +++ b/librecrypt_decompose_chain.3 @@ -0,0 +1,98 @@ +.TH LIBRECRYPT_DECOMPOSE_CHAIN 3 LIBRECRYPT +.SH NAME +librecrypt_decompose_chain - Split a chained password hash string into algorithm components + +.SH SYNOPSIS +.nf +#include <librecrypt.h> + +size_t \fBlibrecrypt_decompose_chain\fP(char *\fIhash\fP, char **\fIchain_out_array\fP, size_t \fIsize\fP); +.fi +.PP +Link with +.IR -lrecrypt . + +.SH DESCRIPTION +The +.BR librecrypt_decompose_chain () +function decomposes the chain of hash algorithms +specified in +.IR hash +into null byte-terminated substrings. The +.IR hash +string is modified by replacing up to +.I size-1 +instances of +.I LIBRECRYPT_ALGORITHM_LINK_DELIMITER +(which is +.BR \(aq>\(aq ) +by null bytes and storing pointers to the +start of each substring in +.IR chain_out_array . +.PP +If +.I size +is positive and smaller than the number of +algorithms, then +.I chain_out_array[size-1] +will contain the remainder of the chain +(still delimited by +.IR LIBRECRYPT_ALGORITHM_LINK_DELIMITER ). +.PP +Unless already stripped out before input, the +last substring ends with the resulting hash. +.PP +The original +.IR hash +string can be restored by overwriting each +terminating null byte in all but the last +substring stored in +.IR chain_out_array +with +.IR LIBRECRYPT_ALGORITHM_LINK_DELIMITER . +.PP +.I hash +must not be +.IR NULL . + +.SH RETURN VALUES +The +.BR librecrypt_decompose_chain () +function returns the number of chained hashes +(the same value as returned by +.BR librecrypt_chain_length (3)). + +.SH ERRORS +The +.BR librecrypt_decompose_chain () +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_decompose_chain () +T} Thread safety MT-Safe +T{ +.BR librecrypt_decompose_chain () +T} Async-signal safety AS-Safe +.TE +.sp + +.SH HISTORY +The +.BR librecrypt_decompose_chain () +function was introduced in version 1.0 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_chain_length (3), +.BR librecrypt_decompose_chain1 (3), +.BR librecrypt_next_algorithm (3) diff --git a/librecrypt_decompose_chain.c b/librecrypt_decompose_chain.c new file mode 100644 index 0000000..9b7aede --- /dev/null +++ b/librecrypt_decompose_chain.c @@ -0,0 +1,153 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +extern inline size_t librecrypt_decompose_chain(char *hash, char **chain_out_array, size_t size); + + +#else + + +#define HASH_1 "a$b" + +#define HASH_2_A "a$b" +#define HASH_2_B "c$d" +#define HASH_2_C "e$f" +#define HASH_2_BC HASH_2_B">"HASH_2_C +#define HASH_2 HASH_2_A">"HASH_2_BC + +#define HASH_3_A "" +#define HASH_3_B "a$b" +#define HASH_3_C "c$d" +#define HASH_3_D "e$f" +#define HASH_3_E "" +#define HASH_3_DE HASH_3_D">"HASH_3_E +#define HASH_3_CDE HASH_3_C">"HASH_3_DE +#define HASH_3_BCDE HASH_3_B">"HASH_3_CDE +#define HASH_3 HASH_3_A">"HASH_3_BCDE + + +#define NULL_OUT(ARRAY)\ + do {\ + for (i = 0u; i < ELEMSOF(ARRAY); i++)\ + (ARRAY)[i] = NULL;\ + } while (0) + + +int +main(void) +{ + char buf[64u]; + char *chain[10u]; + size_t i; + + SET_UP_ALARM(); + + stpcpy(buf, HASH_1); + EXPECT(librecrypt_decompose_chain(buf, chain, 0u) == 1u); + EXPECT(!strcmp(buf, HASH_1)); + NULL_OUT(chain); + EXPECT(librecrypt_decompose_chain(buf, chain, 1u) == 1u); + EXPECT(!strcmp(buf, HASH_1)); + EXPECT(chain[0u] == buf); + NULL_OUT(chain); + EXPECT(librecrypt_decompose_chain(buf, chain, ELEMSOF(chain)) == 1u); + EXPECT(!strcmp(buf, HASH_1)); + EXPECT(chain[0u] == buf); + + stpcpy(buf, HASH_2); + EXPECT(librecrypt_decompose_chain(buf, chain, 0u) == 3u); + EXPECT(!strcmp(buf, HASH_2)); + NULL_OUT(chain); + EXPECT(librecrypt_decompose_chain(buf, chain, 1u) == 3u); + EXPECT(!strcmp(buf, HASH_2)); + EXPECT(chain[0u] == buf); + NULL_OUT(chain); + EXPECT(librecrypt_decompose_chain(buf, chain, 2u) == 3u); + EXPECT(chain[0u] != NULL); + EXPECT(chain[1u] != NULL); + EXPECT(chain[2u] == NULL); + EXPECT(!strcmp(chain[0u], HASH_2_A)); + EXPECT(!strcmp(chain[1u], HASH_2_BC)); + stpcpy(buf, HASH_2); + NULL_OUT(chain); + EXPECT(librecrypt_decompose_chain(buf, chain, ELEMSOF(chain)) == 3u); + EXPECT(chain[0u] != NULL); + EXPECT(chain[1u] != NULL); + EXPECT(chain[2u] != NULL); + EXPECT(chain[3u] == NULL); + EXPECT(!strcmp(chain[0u], HASH_2_A)); + EXPECT(!strcmp(chain[1u], HASH_2_B)); + EXPECT(!strcmp(chain[2u], HASH_2_C)); + + stpcpy(buf, HASH_3); + EXPECT(librecrypt_decompose_chain(buf, chain, 0u) == 5u); + EXPECT(!strcmp(buf, HASH_3)); + NULL_OUT(chain); + EXPECT(librecrypt_decompose_chain(buf, chain, 1u) == 5u); + EXPECT(!strcmp(buf, HASH_3)); + EXPECT(chain[0u] == buf); + NULL_OUT(chain); + EXPECT(librecrypt_decompose_chain(buf, chain, 2u) == 5u); + EXPECT(chain[0u] != NULL); + EXPECT(chain[1u] != NULL); + EXPECT(chain[2u] == NULL); + EXPECT(!strcmp(chain[0u], HASH_3_A)); + EXPECT(!strcmp(chain[1u], HASH_3_BCDE)); + stpcpy(buf, HASH_3); + NULL_OUT(chain); + EXPECT(librecrypt_decompose_chain(buf, chain, 3u) == 5u); + EXPECT(chain[0u] != NULL); + EXPECT(chain[1u] != NULL); + EXPECT(chain[2u] != NULL); + EXPECT(chain[3u] == NULL); + EXPECT(!strcmp(chain[0u], HASH_3_A)); + EXPECT(!strcmp(chain[1u], HASH_3_B)); + EXPECT(!strcmp(chain[2u], HASH_3_CDE)); + stpcpy(buf, HASH_3); + NULL_OUT(chain); + EXPECT(librecrypt_decompose_chain(buf, chain, 4u) == 5u); + EXPECT(chain[0u] != NULL); + EXPECT(chain[1u] != NULL); + EXPECT(chain[2u] != NULL); + EXPECT(chain[3u] != NULL); + EXPECT(chain[4u] == NULL); + EXPECT(!strcmp(chain[0u], HASH_3_A)); + EXPECT(!strcmp(chain[1u], HASH_3_B)); + EXPECT(!strcmp(chain[2u], HASH_3_C)); + EXPECT(!strcmp(chain[3u], HASH_3_DE)); + stpcpy(buf, HASH_3); + NULL_OUT(chain); + EXPECT(librecrypt_decompose_chain(buf, chain, 5u) == 5u); + EXPECT(chain[0u] != NULL); + EXPECT(chain[1u] != NULL); + EXPECT(chain[2u] != NULL); + EXPECT(chain[3u] != NULL); + EXPECT(chain[4u] != NULL); + EXPECT(chain[5u] == NULL); + EXPECT(!strcmp(chain[0u], HASH_3_A)); + EXPECT(!strcmp(chain[1u], HASH_3_B)); + EXPECT(!strcmp(chain[2u], HASH_3_C)); + EXPECT(!strcmp(chain[3u], HASH_3_D)); + EXPECT(!strcmp(chain[4u], HASH_3_E)); + stpcpy(buf, HASH_3); + NULL_OUT(chain); + EXPECT(librecrypt_decompose_chain(buf, chain, 6u) == 5u); + EXPECT(chain[0u] != NULL); + EXPECT(chain[1u] != NULL); + EXPECT(chain[2u] != NULL); + EXPECT(chain[3u] != NULL); + EXPECT(chain[4u] != NULL); + EXPECT(chain[5u] == NULL); + EXPECT(!strcmp(chain[0u], HASH_3_A)); + EXPECT(!strcmp(chain[1u], HASH_3_B)); + EXPECT(!strcmp(chain[2u], HASH_3_C)); + EXPECT(!strcmp(chain[3u], HASH_3_D)); + EXPECT(!strcmp(chain[4u], HASH_3_E)); + + return 0; +} + + +#endif diff --git a/librecrypt_decompose_chain1.3 b/librecrypt_decompose_chain1.3 new file mode 100644 index 0000000..8a6e3b0 --- /dev/null +++ b/librecrypt_decompose_chain1.3 @@ -0,0 +1,75 @@ +.TH LIBRECRYPT_DECOMPOSE_CHAIN1 3 LIBRECRYPT +.SH NAME +librecrypt_decompose_chain1 - Replace algorithm link delimiters with null bytes + +.SH SYNOPSIS +.nf +#include <librecrypt.h> + +size_t \fBlibrecrypt_decompose_chain1\fP(char *\fIhash\fP); +.fi +.PP +Link with +.IR -lrecrypt . + +.SH DESCRIPTION +The +.BR librecrypt_decompose_chain1 () +function replaces each +.I LIBRECRYPT_ALGORITHM_LINK_DELIMITER +(which is +.BR \(aq>\(aq ) +in +.I hash +with a null byte. This effectively decomposes +the string into one string per hash algorithm. +.PP +Unless already stripped out before input, the +last resulting string ends with the resulting +hash, if it was present in the input. +.PP +.I hash +must not be +.IR NULL . + +.SH RETURN VALUES +The +.BR librecrypt_decompose_chain1 () +function returns the number of chained hashes +(the same value as returned by +.BR librecrypt_chain_length (3)). + +.SH ERRORS +The +.BR librecrypt_decompose_chain1 () +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_decompose_chain1 () +T} Thread safety MT-Safe +T{ +.BR librecrypt_decompose_chain1 () +T} Async-signal safety AS-Safe +.TE +.sp + +.SH HISTORY +The +.BR librecrypt_decompose_chain1 () +function was introduced in version 1.0 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_chain_length (3), +.BR librecrypt_decompose_chain (3), +.BR librecrypt_next_algorithm (3) diff --git a/librecrypt_decompose_chain1.c b/librecrypt_decompose_chain1.c new file mode 100644 index 0000000..718e54c --- /dev/null +++ b/librecrypt_decompose_chain1.c @@ -0,0 +1,37 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +extern inline size_t librecrypt_decompose_chain1(char *hash); + + +#else + + +#define CHECK(IN, OUT, N)\ + do {\ + assert(sizeof(IN) <= sizeof(buf));\ + assert(sizeof(IN) == sizeof(OUT));\ + stpcpy(buf, (IN));\ + n = librecrypt_decompose_chain1(buf);\ + EXPECT(n == (N));\ + EXPECT(n == librecrypt_chain_length(IN));\ + EXPECT(!memcmp(buf, (OUT), sizeof(IN)));\ + } while (0) + + +int +main(void) +{ + char buf[64u]; + size_t n; + SET_UP_ALARM(); + CHECK("", "", 1u); + CHECK(">", "\0", 2u); + CHECK("a$b>c$d>e$f", "a$b\0c$d\0e$f", 3u); + return 0; +} + + +#endif diff --git a/librecrypt_encode.3 b/librecrypt_encode.3 new file mode 100644 index 0000000..95862f8 --- /dev/null +++ b/librecrypt_encode.3 @@ -0,0 +1,93 @@ +.TH LIBRECRYPT_ENCODE 3 LIBRECRYPT +.SH NAME +librecrypt_encode - Encode binary salt or hash result into ASCII + +.SH SYNOPSIS +.nf +#include <librecrypt.h> + +size_t \fBlibrecrypt_encode\fP(char *\fIout_buffer\fP, size_t \fIsize\fP, + const void *\fIbinary\fP, size_t \fIlen\fP, + const char \fIlut\fP[restrict static 256], char \fIpad\fP); +.fi +.PP +Link with +.IR -lrecrypt . + +.SH DESCRIPTION +The +.BR librecrypt_encode () +function encodes +.I len +bytes of binary data from +.IR binary +into an ASCII representation written to +.IR out_buffer . +This is used for encoding hash results and salts. +.PP +The +.I lut +argument is an encoding alphabet, consisting +of 64 characters, repeated 4 times. +.PP +The +.I pad +argument specifies the padding character to +use at the end, or the null byte if none. +.PP +On successful completion, up to +.I size +bytes are written to +.IR out_buffer ; +if +.I size +is positive, the output is always +null byte-terminated even if truncated. +.PP +The return value is the number of bytes +that would have been written, excluding +the terminating null byte, if +.I size +was sufficiently large. + +.SH RETURN VALUES +The +.BR librecrypt_encode () +function returns the number of bytes that +would have been written to +.IR out_buffer , +excluding the terminating null byte. + +.SH ERRORS +The +.BR librecrypt_encode () +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_encode () +T} Thread safety MT-Safe +T{ +.BR librecrypt_encode () +T} Async-signal safety AS-Safe +.TE +.sp + +.SH HISTORY +The +.BR librecrypt_encode () +function was introduced in version 1.0 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_decode (3), +.BR librecrypt_get_encoding (3) diff --git a/librecrypt_encode.c b/librecrypt_encode.c new file mode 100644 index 0000000..2199ac1 --- /dev/null +++ b/librecrypt_encode.c @@ -0,0 +1,177 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +#if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +#endif + + +#define O1(I1, I2, I3) ((I1) >> 2) +#define O2(I1, I2, I3) (((I1) << 4) | ((I2) >> 4)) +#define O3(I1, I2, I3) (((I2) << 2) | ((I3) >> 6)) +#define O4(I1, I2, I3) (I3) + + +size_t +librecrypt_encode(char *out_buffer, size_t size, const void *binary, size_t len, + const char lut[restrict static 256], char pad) +{ + const unsigned char *data = binary; + unsigned char a, b, c; + size_t q, r, i, j, n; + + q = len / 3u; + r = len % 3u; + n = q * 4u + (!r ? 0u : pad ? 4u : r + 1u); + + if (!size) + return n; + + size -= 1u; + out_buffer[n < size ? n : size] = '\0'; + + if (r == 1u) { + a = data[q * 3u + 0u]; + switch (size > q * 4u ? size - q * 4u : 0u) { /* fall-through */ + default: + if (pad) + out_buffer[q * 4u + 3u] = pad; + case 3u: + if (pad) + out_buffer[q * 4u + 2u] = pad; + case 2u: + out_buffer[q * 4u + 1u] = lut[O2(a, 0, 0) & 255]; + case 1u: + out_buffer[q * 4u + 0u] = lut[O1(a, 0, 0) & 255]; + case 0u: + break; + } + } else if (r == 2u) { + a = data[q * 3u + 0u]; + b = data[q * 3u + 1u]; + switch (size > q * 4u ? size - q * 4u : 0u) { /* fall-through */ + default: + if (pad) + out_buffer[q * 4u + 3u] = pad; + case 3u: + out_buffer[q * 4u + 2u] = lut[O3(a, b, 0) & 255]; + case 2u: + out_buffer[q * 4u + 1u] = lut[O2(a, b, 0) & 255]; + case 1u: + out_buffer[q * 4u + 0u] = lut[O1(a, b, 0) & 255]; + case 0u: + break; + } + } + + i = q * 4u; + j = q * 3u; + if (i > size) { + q = size / 4u; + r = size % 4u; + i = q * 4u; + j = q * 3u; + c = data[j + 2u]; + b = data[j + 1u]; + a = data[j + 0u]; + switch (r) { /* fall-through */ + case 3u: + out_buffer[i + 2u] = lut[O3(a, b, c) & 255]; + case 2u: + out_buffer[i + 1u] = lut[O2(a, b, c) & 255]; + case 1u: + out_buffer[i + 0u] = lut[O1(a, b, c) & 255]; + default: + break; + } + } + while (i) { + i -= 4u; + j -= 3u; + c = data[j + 2u]; + b = data[j + 1u]; + a = data[j + 0u]; + out_buffer[i + 3u] = lut[O4(a, b, c) & 255]; + out_buffer[i + 2u] = lut[O3(a, b, c) & 255]; + out_buffer[i + 1u] = lut[O2(a, b, c) & 255]; + out_buffer[i + 0u] = lut[O1(a, b, c) & 255]; + } + + return n; +} + + +#else + + +NONSTRING static const char lut[256u] = MAKE_ENCODING_LUT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); + + +#define CHECK(BINARY, ASCII)\ + check((BINARY), sizeof(BINARY) - 1u, (ASCII), sizeof(ASCII) - 1u) + + +static void +check(const char *binary, size_t binary_len, const char *ascii, size_t ascii_len) +{ + size_t padded_ascii_len = ascii_len; + char buf[256u]; + size_t i, j, n; + + if (padded_ascii_len & 3u) { + padded_ascii_len |= 3u; + padded_ascii_len += 1u; + } + assert(padded_ascii_len % 4u == 0u); + assert(padded_ascii_len / 4u == (ascii_len + 3u) / 4u); + assert(padded_ascii_len >= ascii_len); + + EXPECT(librecrypt_encode(NULL, 0u, binary, binary_len, lut, '\0') == ascii_len); + EXPECT(librecrypt_encode(buf, 0u, binary, binary_len, lut, '\0') == ascii_len); + + EXPECT(librecrypt_encode(NULL, 0u, binary, binary_len, lut, '=') == padded_ascii_len); + EXPECT(librecrypt_encode(buf, 0u, binary, binary_len, lut, '=') == padded_ascii_len); + + for (i = 0u; i <= ascii_len; i++) { + memset(buf, 99, sizeof(buf)); + EXPECT(librecrypt_encode(buf, i + 1u, binary, binary_len, lut, '\0') == ascii_len); + EXPECT(!memcmp(buf, ascii, i)); + EXPECT(buf[i] == '\0'); + for (j = i + 1u; j < sizeof(buf); j++) + EXPECT(buf[j] == 99); + } + + for (i = 0u; i <= padded_ascii_len; i++) { + memset(buf, 99, sizeof(buf)); + EXPECT(librecrypt_encode(buf, i + 1u, binary, binary_len, lut, '=') == padded_ascii_len); + j = i < ascii_len ? i : ascii_len; + n = i < padded_ascii_len ? i : padded_ascii_len; + EXPECT(!memcmp(buf, ascii, j)); + for (; j < n; j++) + EXPECT(buf[j] == '='); + EXPECT(buf[j++] == '\0'); + for (; j < sizeof(buf); j++) + EXPECT(buf[j] == 99); + } +} + + +int +main(void) +{ + SET_UP_ALARM(); + CHECK("", ""); + CHECK("\x00", "AA"); + CHECK("\x00\x00", "AAA"); + CHECK("\x00\x00\x00", "AAAA"); + CHECK("12345678", "MTIzNDU2Nzg"); + CHECK("testtest", "dGVzdHRlc3Q"); + CHECK("zy[]y21 !", "enlbXXkyMSAh"); + CHECK("{~|~}~~~\x7f\x7f", "e358fn1+fn5/fw"); + return 0; +} + + +#endif diff --git a/librecrypt_equal.3 b/librecrypt_equal.3 new file mode 100644 index 0000000..cc3a387 --- /dev/null +++ b/librecrypt_equal.3 @@ -0,0 +1,70 @@ +.TH LIBRECRYPT_EQUAL 3 LIBRECRYPT +.SH NAME +librecrypt_equal - Compare strings in constant time + +.SH SYNOPSIS +.nf +#include <librecrypt.h> + +int \fBlibrecrypt_equal\fP(const char *\fIa\fP, const char *\fIb\fP); +.fi +.PP +Link with +.IR -lrecrypt . + +.SH DESCRIPTION +The +.BR librecrypt_equal () +function compares two null byte-terminated strings +in constant time. +.PP +If the strings are of different length, the function +returns 0 after the lengths have been measured. +This should not happen when comparing password hashes, +since their length is determined by the selected +configuration. +.PP +.I a +and +.I b +must not be +.IR NULL . + +.SH RETURN VALUES +The +.BR librecrypt_equal () +function returns 1 if the strings are equal, +and 0 otherwise. + +.SH ERRORS +The +.BR librecrypt_equal () +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_equal () +T} Thread safety MT-Safe +T{ +.BR librecrypt_equal () +T} Async-signal safety AS-Safe +.TE +.sp + +.SH HISTORY +The +.BR librecrypt_equal () +function was introduced in version 1.0 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_equal_binary (3) diff --git a/librecrypt_equal.c b/librecrypt_equal.c new file mode 100644 index 0000000..22e2c05 --- /dev/null +++ b/librecrypt_equal.c @@ -0,0 +1,39 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +extern inline int librecrypt_equal(const char *a, const char *b); + + +#else + + +int +main(void) +{ + SET_UP_ALARM(); + EXPECT(librecrypt_equal("", "") == 1); + EXPECT(librecrypt_equal("", "a") == 0); + EXPECT(librecrypt_equal("a", "") == 0); + EXPECT(librecrypt_equal("a", "b") == 0); + EXPECT(librecrypt_equal("a", "a") == 1); + EXPECT(librecrypt_equal("abcdef", "Abcdef") == 0); + EXPECT(librecrypt_equal("abcdef", "ABcdef") == 0); + EXPECT(librecrypt_equal("abcdef", "aBcdef") == 0); + EXPECT(librecrypt_equal("abcdef", "abCdef") == 0); + EXPECT(librecrypt_equal("abcdef", "abCdeF") == 0); + EXPECT(librecrypt_equal("abcdef", "abcdeF") == 0); + EXPECT(librecrypt_equal("abcdef", "abcdex") == 0); + EXPECT(librecrypt_equal("abcdef", "abcxef") == 0); + EXPECT(librecrypt_equal("abcdef", "xbcdef") == 0); + EXPECT(librecrypt_equal("abcdef", "abcdef") == 1); + EXPECT(librecrypt_equal("abcdef", "abcdefg") == 0); + EXPECT(librecrypt_equal("abcdefg", "abcdef") == 0); + EXPECT(librecrypt_equal("abcdef", "") == 0); + EXPECT(librecrypt_equal("", "abcdef") == 0); + return 0; +} + + +#endif diff --git a/librecrypt_equal_binary.3 b/librecrypt_equal_binary.3 new file mode 100644 index 0000000..d441c5b --- /dev/null +++ b/librecrypt_equal_binary.3 @@ -0,0 +1,65 @@ +.TH LIBRECRYPT_EQUAL_BINARY 3 LIBRECRYPT +.SH NAME +librecrypt_equal_binary - Compare memory segments in constant time + +.SH SYNOPSIS +.nf +#include <librecrypt.h> + +int \fBlibrecrypt_equal_binary\fP(const void *\fIa\fP, const void *\fIb\fP, size_t \fIlen\fP); +.fi +.PP +Link with +.IR -lrecrypt . + +.SH DESCRIPTION +The +.BR librecrypt_equal_binary () +function compares the first +.I len +bytes of +.I a +and +.I b +in constant time. + +.SH RETURN VALUES +The +.BR librecrypt_equal_binary () +function returns 1 if the memory segments are +equal in their first +.I len +bytes, and 0 otherwise. + +.SH ERRORS +The +.BR librecrypt_equal_binary () +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_equal_binary () +T} Thread safety MT-Safe +T{ +.BR librecrypt_equal_binary () +T} Async-signal safety AS-Safe +.TE +.sp + +.SH HISTORY +The +.BR librecrypt_equal_binary () +function was introduced in version 1.0 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_equal (3) diff --git a/librecrypt_equal_binary.c b/librecrypt_equal_binary.c new file mode 100644 index 0000000..e1d17cd --- /dev/null +++ b/librecrypt_equal_binary.c @@ -0,0 +1,77 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +#if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wsuggest-attribute=const" +# pragma GCC diagnostic ignored "-Wsuggest-attribute=pure" +#endif + + +static void +explicit(unsigned char r) +{ + (void) r; +#if defined(__GNUC__) + __asm__ volatile ("" :: "g" (r) : "memory"); +#endif +} + + +void (*volatile librecrypt_explicit_____)(unsigned char) = &explicit; + + +int +librecrypt_equal_binary(const void *a, const void *b, size_t len) +{ + const unsigned char *x = a; + const unsigned char *y = b; + size_t i; + unsigned char r = 0u; + + for (i = 0u; i < len; i++) + r = (unsigned char)(r | (*x++ ^ *y++)); + + (*librecrypt_explicit_____)(r); + + return r ? 0 : 1; +} + + +#else + + +int +main(void) +{ + SET_UP_ALARM(); + EXPECT(librecrypt_equal_binary("", "", 0u) == 1); + EXPECT(librecrypt_equal_binary("", "", 1u) == 1); + EXPECT(librecrypt_equal_binary("a", "", 1u) == 0); + EXPECT(librecrypt_equal_binary("", "a", 1u) == 0); + EXPECT(librecrypt_equal_binary("a", "a", 1u) == 1); + EXPECT(librecrypt_equal_binary("a", "a", 2u) == 1); + EXPECT(librecrypt_equal_binary("abcdef", "abcdef", 6u) == 1); + EXPECT(librecrypt_equal_binary("abcdef", "Abcdef", 6u) == 0); + EXPECT(librecrypt_equal_binary("abcdef", "AbCdef", 6u) == 0); + EXPECT(librecrypt_equal_binary("abcdef", "abCdef", 6u) == 0); + EXPECT(librecrypt_equal_binary("abcdef", "abCdeF", 6u) == 0); + EXPECT(librecrypt_equal_binary("abcdef", "abcdeF", 6u) == 0); + EXPECT(librecrypt_equal_binary("abcdef", "xbcdef", 6u) == 0); + EXPECT(librecrypt_equal_binary("abcdef", "xbxdef", 6u) == 0); + EXPECT(librecrypt_equal_binary("abcdef", "abxdef", 6u) == 0); + EXPECT(librecrypt_equal_binary("abcdef", "abxdex", 6u) == 0); + EXPECT(librecrypt_equal_binary("abcdef", "abcdex", 6u) == 0); + EXPECT(librecrypt_equal_binary("abcdef", "abcdex", 5u) == 1); + EXPECT(librecrypt_equal_binary("abcdef", "abcdex", 4u) == 1); + EXPECT(librecrypt_equal_binary("abcdef", "abcdex", 3u) == 1); + EXPECT(librecrypt_equal_binary("abcdef", "abcdex", 2u) == 1); + EXPECT(librecrypt_equal_binary("abcdef", "abcdex", 1u) == 1); + EXPECT(librecrypt_equal_binary("abcdef", "abcdex", 0u) == 1); + EXPECT(librecrypt_equal_binary(NULL, NULL, 0u) == 1); + return 0; +} + + +#endif diff --git a/librecrypt_fill_with_random_.c b/librecrypt_fill_with_random_.c new file mode 100644 index 0000000..475a156 --- /dev/null +++ b/librecrypt_fill_with_random_.c @@ -0,0 +1,142 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +librecrypt_fill_with_random_(void *out, size_t n, ssize_t (*rng)(void *out, size_t n, void *user), void *user) +{ + char *buf = out; + ssize_t r; + + if (!rng) + rng = &librecrypt_rng_; + + while (n) { + r = (*rng)(buf, n, user); + if (r <= 0) { + if (!r) + abort(); + return -1; + } + buf = &buf[(size_t)r]; + n -= (size_t)r; + } + + return 0; +} + + +#else + + +static ssize_t +zero(void *out, size_t n, void *user) +{ + (void) user; + memset(out, 0, n); + return (ssize_t)n; +} + + +static ssize_t +seq(void *out, size_t n, void *user) +{ + unsigned char *restrict buf = out; + unsigned char *restrict num = user; + size_t i; + if (n > 64u) + n = 64u; + for (i = 0u; i < n; i++) + buf[i] = (*num)++; + return (ssize_t)n; +} + + +static ssize_t +next(void *out, size_t n, void *user) +{ + unsigned char *restrict buf = out; + unsigned char *restrict num = user; + assert(n); + *buf = (*num)++; + return 1; +} + + +static ssize_t +failer(void *out, size_t n, void *user) +{ + (void) out; + (void) n; + (void) user; + errno = EDOM; + return -1; +} + + +static ssize_t +zero_ret(void *out, size_t n, void *user) +{ + (void) out; + (void) n; + (void) user; + return 0; +} + + +int +main(void) +{ + unsigned char buf1[1024u]; + unsigned char buf2[sizeof(buf1)]; + unsigned char s; + size_t i; + int rv = 0; + + INIT_TEST_ABORT(); + + SET_UP_ALARM(); + + EXPECT(librecrypt_fill_with_random_(buf1, sizeof(buf1), NULL, NULL) == 0); + EXPECT(librecrypt_fill_with_random_(buf2, sizeof(buf1), NULL, NULL) == 0); + EXPECT(memcmp(buf1, buf2, sizeof(buf1))); + + memset(buf1, 99, sizeof(buf1)); + errno = 0; + EXPECT(librecrypt_fill_with_random_(buf1, sizeof(buf1), &zero, NULL) == 0); + EXPECT(errno == 0); + for (s = 0u, i = 0u; i < sizeof(buf1); i++, s++) + EXPECT(!buf1[i]); + + memset(buf1, 99, sizeof(buf1)); + s = 0u; + errno = 0; + EXPECT(librecrypt_fill_with_random_(buf1, sizeof(buf1), &seq, &s) == 0); + EXPECT(errno == 0); + for (s = 0u, i = 0u; i < sizeof(buf1); i++, s++) + EXPECT(buf1[i] == s); + + memset(buf1, 99, sizeof(buf1)); + s = 0u; + errno = 0; + EXPECT(librecrypt_fill_with_random_(buf1, sizeof(buf1), &next, &s) == 0); + EXPECT(errno == 0); + for (s = 0u, i = 0u; i < sizeof(buf1); i++, s++) + EXPECT(buf1[i] == s); + + memset(buf1, 99, sizeof(buf1)); + s = 0u; + errno = 0; + EXPECT(librecrypt_fill_with_random_(buf1, sizeof(buf1), &failer, NULL) == -1); + EXPECT(errno == EDOM); + for (s = 0u, i = 0u; i < sizeof(buf1); i++, s++) + EXPECT(buf1[i] == 99); + + EXPECT_ABORT(rv = librecrypt_fill_with_random_(buf1, sizeof(buf1), &zero_ret, NULL)); + + return rv; +} + + +#endif diff --git a/librecrypt_find_first_algorithm_.c b/librecrypt_find_first_algorithm_.c new file mode 100644 index 0000000..406f5ac --- /dev/null +++ b/librecrypt_find_first_algorithm_.c @@ -0,0 +1,41 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +const struct algorithm * +librecrypt_find_first_algorithm_(const char *settings, size_t len) +{ + unsigned r, priority = 0; + const struct algorithm *algo, *found = NULL; + size_t i; + + for (i = 0u;; i++) { + algo = &librecrypt_algorithms_[i]; + if (IS_END_OF_ALGORITHMS(algo)) + break; + r = (*algo->is_algorithm)(settings, len); + if (r > priority) { + priority = r; + found = algo; + } + } + + return found; +} + + +#else + + +int +main(void) +{ + SET_UP_ALARM(); + + return 0; +} + + +#endif +/* TODO test */ diff --git a/librecrypt_get_encoding.3 b/librecrypt_get_encoding.3 new file mode 100644 index 0000000..e9f7e83 --- /dev/null +++ b/librecrypt_get_encoding.3 @@ -0,0 +1,112 @@ +.TH LIBRECRYPT_GET_ENCODING 3 LIBRECRYPT +.SH NAME +librecrypt_get_encoding - Get encoding alphabet for the last algorithm in a chain + +.SH SYNOPSIS +.nf +#include <librecrypt.h> + +const void *\fBlibrecrypt_get_encoding\fP(const char *\fIsettings\fP, size_t \fIlen\fP, + char *\fIpad_out\fP, int *\fIstrict_pad_out\fP, int \fIdecoding\fP); +.fi +.PP +Link with +.IR -lrecrypt . + +.SH DESCRIPTION +The +.BR librecrypt_get_encoding () +function returns the ASCII encoding used for +the last algorithm in the algorithm chain +specified by +.IR settings . +.PP +If +.I decoding +is zero, the returned pointer is suitable for +use as the +.I lut +parameter to +.BR librecrypt_encode (3) +and points to an encoding alphabet consisting +of 64 characters repeated 4 times. +.PP +If +.I decoding +is non-zero, the returned pointer is suitable +for use as the +.I lut +parameter to +.BR librecrypt_decode (3) +and points to a reverse lookup table mapping +any valid character (except the padding +character) to its value in the encoding +alphabet and any other character to +.BR 0xFFu . +.PP +On success, +.IR *pad_out +is set to the padding character, or to the +null byte if there is no padding. +.IR *strict_pad_out +is set to 1 if padding is mandatory, +0 otherwise. +.IR *strict_pad_out +is set arbitrarily to 0 or 1 if +.IR *pad_out +is set to the null byte. +.PP +.I pad_out +and +.I strict_pad_out +must not be +.IR NULL . + +.SH RETURN VALUES +The +.BR librecrypt_get_encoding () +function returns a pointer to the encoding +data, or +.IR NULL +on failure. On failure, +.IR errno +is set to describe the error. + +.SH ERRORS +The +.BR librecrypt_get_encoding () +function will fail if: +.TP +.B ENOSYS +The last algorithm in +.I settings +is not recognised or was disabled at compile-time. + +.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_get_encoding () +T} Thread safety MT-Safe +T{ +.BR librecrypt_get_encoding () +T} Async-signal safety AS-Safe +.TE +.sp + +.SH HISTORY +The +.BR librecrypt_get_encoding () +function was introduced in version 1.0 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_encode (3), +.BR librecrypt_decode (3) diff --git a/librecrypt_get_encoding.c b/librecrypt_get_encoding.c new file mode 100644 index 0000000..dd5d33d --- /dev/null +++ b/librecrypt_get_encoding.c @@ -0,0 +1,46 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +const void * +librecrypt_get_encoding(const char *settings, size_t len, char *pad_out, int *strict_pad_out, int decoding) +{ + size_t i, start = 0u; + const struct algorithm *algo; + + for (i = 0u; i < len; i++) + if (settings[i] == LIBRECRYPT_ALGORITHM_LINK_DELIMITER) + start = i + 1u; + settings = &settings[start]; + len -= start; + + algo = librecrypt_find_first_algorithm_(settings, len); + if (!algo) { + errno = ENOSYS; + return NULL; + } + + *pad_out = algo->pad; + *strict_pad_out = algo->strict_pad; + if (decoding) + return algo->decoding_lut; + else + return algo->encoding_lut; +} + + +#else + + +int +main(void) +{ + SET_UP_ALARM(); + + return 0; +} + + +#endif +/* TODO test */ diff --git a/librecrypt_hash.3 b/librecrypt_hash.3 new file mode 100644 index 0000000..5bf32ea --- /dev/null +++ b/librecrypt_hash.3 @@ -0,0 +1,135 @@ +.TH LIBRECRYPT_HASH 3 LIBRECRYPT +.SH NAME +librecrypt_hash - Compute password hash encoded in ASCII without settings prefix + +.SH SYNOPSIS +.nf +#include <librecrypt.h> + +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, void *\fIreserved\fP); +.fi +.PP +Link with +.IR -lrecrypt . +Static linking may require additional flags +depending on enabled hash algorithms. + +.SH DESCRIPTION +The +.BR librecrypt_hash () +function computes the hash of a password. +The resulting hash is encoded with +.BR librecrypt_encode (3) +but stored without any settings prefix. +.PP +The password is provided in +.IR phrase +and can contain null bytes; its length is specified by +.IR len . +.PP +The password hash configuration string is provided in +.IR settings . +If +.I settings +contains a resulting hash, it is ignored. +If +.I settings +uses asterisk-encoding to specify random salts, +the function fails. +.PP +The +.I reserved +parameter is reserved for future use and should be +.IR NULL . +.PP +On successful completion, if +.I size +is positive, the output is +null byte-terminated even if truncated. +.PP +Any encountered +.BR EINTR +is ignored. +.PP +On failure, +.I out_buffer +remains unmodified. +.PP +.I settings +must not be +.IR NULL . + +.SH RETURN VALUES +The +.BR librecrypt_hash () +function returns the number of bytes that would +have been written to +.IR out_buffer +if +.I size +was sufficiently large, excluding the terminating +null byte. On failure, -1 is returned and +.I errno +is set to describe the error. + +.SH ERRORS +The +.BR librecrypt_hash () +function will fail if: +.TP +.B EINVAL +.I reserved +is not +.IR NULL . +.TP +.B EINVAL +.I settings +is invalid. +.TP +.B EINVAL +.I settings +uses asterisk-encoding to specify random salts. +.TP +.B ERANGE +.I len +is too large or too small for the selected +initial algorithm. +.TP +.B ENOMEM +Failed to allocate internal scratch memory. +.TP +.B ENOSYS +A selected hash algorithm is not recognised or +was disabled at compile-time. + +.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_hash () +T} Thread safety MT-Safe +T{ +.BR librecrypt_hash () +T} Async-signal safety AS-Unsafe +.TE +.sp + +.SH HISTORY +The +.BR librecrypt_hash () +function was introduced in version 1.0 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_hash_binary (3), +.BR librecrypt_crypt (3), +.BR librecrypt_test_supported (3) diff --git a/librecrypt_hash.c b/librecrypt_hash.c new file mode 100644 index 0000000..267aa31 --- /dev/null +++ b/librecrypt_hash.c @@ -0,0 +1,26 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +ssize_t +librecrypt_hash(char *restrict out_buffer, size_t size, const char *phrase, size_t len, const char *settings, void *reserved) +{ + return librecrypt_hash_(out_buffer, size, phrase, len, settings, reserved, ASCII_HASH); +} + + +#else + + +int +main(void) +{ + SET_UP_ALARM(); + + return 0; +} + + +#endif +/* TODO test */ diff --git a/librecrypt_hash_.c b/librecrypt_hash_.c new file mode 100644 index 0000000..ed842e9 --- /dev/null +++ b/librecrypt_hash_.c @@ -0,0 +1,187 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +static ssize_t +zero_generator(void *out, size_t n, void *user) +{ + (void) user; + if (n > (size_t)SSIZE_MAX) + n = (size_t)SSIZE_MAX; + memset(out, 0, n); + return (ssize_t)n; +} + + +ssize_t +librecrypt_hash_(char *restrict out_buffer, size_t size, const char *phrase, size_t len, + const char *settings, void *reserved, enum action action) +{ + const struct algorithm *algo; + ssize_t (*rng)(void *out, size_t n, void *user) = NULL; + char *settings_scratch = NULL; + char *phrase_scratches[2] = {NULL, NULL}; + size_t phrase_scratch_sizes[2] = {0u, 0u}; + size_t n, ascii_len, min, prefix, ret = 0u; + int has_next, phrase_scratch_i = 0; + ssize_t r_len; + int r; + void *new; + + if (reserved != NULL) { + errno = EINVAL; + return -1; + } + + if (!size) + rng = &zero_generator; + + if (strchr(settings, '*')) { + if (action != ASCII_CRYPT) { + errno = EINVAL; + return -1; + } + r_len = librecrypt_realise_salts(out_buffer, size, settings, rng, NULL); + if (r_len < 0) { + if (errno == ERANGE) + errno = ENOMEM; + return -1; + } else if ((size_t)r_len >= size) { + settings_scratch = malloc((size_t)r_len + 1u); + if (!settings_scratch) + return -1; + if (librecrypt_realise_salts(settings_scratch, (size_t)r_len + 1u, settings, rng, NULL) != r_len) + abort(); + settings = settings_scratch; + } + } + +next: + has_next = 0; + for (n = 0u; settings[n]; n++) { + if (settings[n] == LIBRECRYPT_ALGORITHM_LINK_DELIMITER) { + has_next = 1; + break; + } + } + + algo = librecrypt_find_first_algorithm_(settings, n); + if (!algo) { + errno = ENOSYS; + goto fail; + } + + prefix = (*algo->get_prefix)(settings, n); + if (has_next && prefix < n) { + errno = EINVAL; + goto fail; + } + + if (action == ASCII_CRYPT) { + min = size ? size - 1u < prefix ? size - 1u : prefix : 0u; + size -= min; + memcpy(out_buffer, settings, min); + out_buffer = &out_buffer[min]; + ret += prefix; + } + + if (size && phrase_scratch_sizes[phrase_scratch_i] < algo->hash_size) { + librecrypt_wipe(phrase_scratches[phrase_scratch_i], phrase_scratch_sizes[phrase_scratch_i]); + new = realloc(phrase_scratches[phrase_scratch_i], algo->hash_size); + if (!new) { + free(phrase_scratches[phrase_scratch_i]); + phrase_scratches[phrase_scratch_i] = NULL; + phrase_scratch_sizes[phrase_scratch_i] = 0u; + goto fail; + } + phrase_scratches[phrase_scratch_i] = new; + phrase_scratch_sizes[phrase_scratch_i] = algo->hash_size; + } + + if (has_next) { + hash_to_scratch: + r = (*algo->hash)(size ? phrase_scratches[phrase_scratch_i] : NULL, + size ? phrase_scratch_sizes[phrase_scratch_i] : 0u, + phrase, len, settings, prefix, reserved); + } else if (action == BINARY_HASH) { + hash_to_output: + r = (*algo->hash)(out_buffer, size, phrase, len, settings, prefix, reserved); + } else if (size < algo->hash_size) { + goto hash_to_scratch; + } else { + goto hash_to_output; + } + if (r < 0) + goto fail; + + if (!has_next) { + if (action == BINARY_HASH) { + ret += algo->hash_size; + } else if (!size) { + ascii_len = algo->hash_size % 3u; + if (ascii_len) { + if (algo->pad && algo->strict_pad) + ascii_len = 4u; + else + ascii_len += 1u; + } + ascii_len += algo->hash_size / 3u * 4u; + goto include_ascii; + } else { + ascii_len = librecrypt_encode(out_buffer, size, + size < algo->hash_size ? phrase_scratches[phrase_scratch_i] : out_buffer, + algo->hash_size, algo->encoding_lut, algo->strict_pad ? algo->pad : '\0'); + include_ascii: + min = size ? size - 1u < ascii_len ? size - 1u : ascii_len : 0u; + out_buffer = &out_buffer[min]; + size -= min; + ret += ascii_len; + } + } else { + phrase = size ? phrase_scratches[phrase_scratch_i] : NULL; + phrase_scratch_i ^= 1; + len = algo->hash_size; + + settings = &settings[n]; + if (action == ASCII_CRYPT) { + ret += 1u; + if (size) { + *out_buffer++ = LIBRECRYPT_ALGORITHM_LINK_DELIMITER; + size -= 1u; + } + } + settings++; + goto next; + } + + librecrypt_wipe(phrase_scratches[0u], phrase_scratch_sizes[0u]); + librecrypt_wipe(phrase_scratches[1u], phrase_scratch_sizes[1u]); + librecrypt_wipe_str(settings_scratch); + free(phrase_scratches[0u]); + free(phrase_scratches[1u]); + free(settings_scratch); + + if (size && action != BINARY_HASH) + out_buffer[0] = '\0'; + return (ssize_t)ret; + +fail: + librecrypt_wipe(phrase_scratches[0u], phrase_scratch_sizes[0u]); + librecrypt_wipe(phrase_scratches[1u], phrase_scratch_sizes[1u]); + librecrypt_wipe_str(settings_scratch); + free(phrase_scratches[0u]); + free(phrase_scratches[1u]); + free(settings_scratch); + return -1; +} + + +#else + + +/* Tested via librecrypt_hash_binary, librecrypt_hash, and librecrypt_crypt */ +CONST int main(void) { return 0; } + + +#endif diff --git a/librecrypt_hash_binary.3 b/librecrypt_hash_binary.3 new file mode 100644 index 0000000..720b82d --- /dev/null +++ b/librecrypt_hash_binary.3 @@ -0,0 +1,138 @@ +.TH LIBRECRYPT_HASH_BINARY 3 LIBRECRYPT +.SH NAME +librecrypt_hash_binary - Compute password hash in raw binary form + +.SH SYNOPSIS +.nf +#include <librecrypt.h> + +ssize_t \fBlibrecrypt_hash_binary\fP(char *restrict \fIout_buffer\fP, size_t \fIsize\fP, + const char *\fIphrase\fP, size_t \fIlen\fP, + const char *\fIsettings\fP, void *\fIreserved\fP); +.fi +.PP +Link with +.IR -lrecrypt . +Static linking may require additional flags +depending on enabled hash algorithms. + +.SH DESCRIPTION +The +.BR librecrypt_hash_binary () +function computes the hash of a password. +The hash is stored in raw binary format without any +settings prefix. +.PP +The password is provided in +.IR phrase +and can contain null bytes; its length is specified by +.IR len . +.PP +The password hash configuration string is provided in +.IR settings . +If +.I settings +contains a resulting hash, it is ignored. +If +.I settings +uses asterisk-encoding to specify random salts, +the function fails. +.PP +The +.I reserved +parameter is reserved for future use and should be +.IR NULL . +.PP +On successful completion, up to +.I size +bytes are written to +.IR out_buffer ; +the return value describes how many bytes would have +been written if +.I size +was sufficiently large. +.PP +Any encountered +.BR EINTR +is ignored. +.PP +On failure, +.I out_buffer +remains unmodified. +.PP +.I settings +must not be +.IR NULL . + +.SH RETURN VALUES +The +.BR librecrypt_hash_binary () +function returns the number of bytes that would +have been written to +.IR out_buffer +if +.I size +was sufficiently large. +On failure, -1 is returned and +.I errno +is set to describe the error. + +.SH ERRORS +The +.BR librecrypt_hash_binary () +function will fail if: +.TP +.B EINVAL +.I reserved +is not +.IR NULL . +.TP +.B EINVAL +.I settings +is invalid. +.TP +.B EINVAL +.I settings +uses asterisk-encoding to specify random salts. +.TP +.B ERANGE +.I len +is too large or too small for the selected +initial algorithm. +.TP +.B ENOMEM +Failed to allocate internal scratch memory. +.TP +.B ENOSYS +A selected hash algorithm is not recognised or +was disabled at compile-time. + +.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_hash_binary () +T} Thread safety MT-Safe +T{ +.BR librecrypt_hash_binary () +T} Async-signal safety AS-Unsafe +.TE +.sp + +.SH HISTORY +The +.BR librecrypt_hash_binary () +function was introduced in version 1.0 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_hash (3), +.BR librecrypt_crypt (3), +.BR librecrypt_test_supported (3) diff --git a/librecrypt_hash_binary.c b/librecrypt_hash_binary.c new file mode 100644 index 0000000..c119c78 --- /dev/null +++ b/librecrypt_hash_binary.c @@ -0,0 +1,26 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +ssize_t +librecrypt_hash_binary(char *restrict out_buffer, size_t size, const char *phrase, size_t len, const char *settings, void *reserved) +{ + return librecrypt_hash_(out_buffer, size, phrase, len, settings, reserved, BINARY_HASH); +} + + +#else + + +int +main(void) +{ + SET_UP_ALARM(); + + return 0; +} + + +#endif +/* TODO test */ diff --git a/librecrypt_make_settings.3 b/librecrypt_make_settings.3 new file mode 100644 index 0000000..f742df6 --- /dev/null +++ b/librecrypt_make_settings.3 @@ -0,0 +1,177 @@ +.TH LIBRECRYPT_MAKE_SETTINGS 3 LIBRECRYPT +.SH NAME +librecrypt_make_settings - Generate a password hash settings string + +.SH SYNOPSIS +.nf +#include <librecrypt.h> + +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); +.fi +.PP +Link with +.IR -lrecrypt . +Static linking may require additional flags +depending on enabled hash algorithms. + +.SH DESCRIPTION +The +.BR librecrypt_make_settings () +function generates a password hash setting string +and writes it to +.IR out_buffer . +.PP +If +.I algorithm +is +.IR NULL , +a default strong algorithm is selected. +The empty string selects the deprecated DES algorithm. +Algorithm chains are disallowed for this function. +.PP +The +.I memcost +parameter specifies an approximate memory cost in bytes. +A default value is selected if 0 is specified. +.PP +The +.I timecost +parameter specifies a total time cost in an +implementation-dependent, monotonic unit. +A default value is selected if 0 is specified. +.PP +If +.I gensalt +is non-zero, random salts are included in the +generated output, otherwise the output will only +specify the sizes the salts shall have. +.PP +If +.I gensalt +is non-zero, +.IR *rng , +or a default implementation (which tries to use +cryptographic random number generation but has a +non-cryptographic random number generation as a +last resort) if +.I rng +is +.IR NULL , +used to generate random bytes. +.I *rng +shall fill +.I out +with at least 1, and up to +.IR n , +random bytes, and return the number of bytes +written, or return -1 on failure. +.PP +The +.I data +argument is passed into the +.IR *rng -parameter +with the same name, as is and may be used by +.IR *rng +for user-defined purposes. +.PP +On successful completion, if +.I size +is positive, the output is null byte terminated +even if truncated. +.PP +If +.I rng +is +.IR NULL , +any encountered +.BR EINTR +is ignored (cannot happen if +.I gensalt +is zero); if it is encountered, +.I errno +will be set to +.BR EINTR , +but is otherwise left unmodified unless the function +fails due to one of the errors listed below. + +.SH RETURN VALUES +The +.BR librecrypt_make_settings () +function returns the number of bytes that would have +been written to +.IR out_buffer +if +.I size +was sufficiently large, or -1 on failure and +.I errno +is set to describe the error. + +.SH ERRORS +The +.BR librecrypt_make_settings () +function will fail if: +.TP +.B EINVAL +.I algorithm +represents a chain of algorithms. +.TP +.B ENOSYS +.I algorithm +represents an algorithm that is not recognised or +was disabled at compile-time. +.TP +.B ENOSYS +.I algorithm +is +.IR NULL +but all algorithms were disabled at compile-time. +.PP +Additionally, unless +.I rng +is +.IR NULL , +.I *rng +may set +.I errno +(if +.I gensalt +is non-zero). + +.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_make_settings () +T} Thread safety MT-Safe +T{ +.BR librecrypt_make_settings () +T} Async-signal safety AS-Unsafe +.TE +.sp +.PP +If +.I gensalt +is non-zero and +.I rng +is +.RI non- NULL , +.I *rng +may weaken the safety attributes. + +.SH HISTORY +The +.BR librecrypt_make_settings () +function was introduced in version 1.0 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_realise_salts (3) diff --git a/librecrypt_make_settings.c b/librecrypt_make_settings.c new file mode 100644 index 0000000..99c7e3b --- /dev/null +++ b/librecrypt_make_settings.c @@ -0,0 +1,50 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +ssize_t +librecrypt_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) +{ + const struct algorithm *algo; + + if (!algorithm) { + algo = &librecrypt_algorithms_[0]; + if (IS_END_OF_ALGORITHMS(algo)) + goto enosys; + } else { + if (strchr(algorithm, LIBRECRYPT_ALGORITHM_LINK_DELIMITER)) { + errno = EINVAL; + return -1; + } + algo = librecrypt_find_first_algorithm_(algorithm, strlen(algorithm)); + if (!algo) + goto enosys; + } + + if (!rng) + rng = &librecrypt_rng_; + + return (*algo->make_settings)(out_buffer, size, algorithm, memcost, timecost, gensalt, rng, user); + +enosys: + errno = ENOSYS; + return -1; +} + + +#else + + +int +main(void) +{ + SET_UP_ALARM(); + + return 0; +} + + +#endif +/* TODO test */ diff --git a/librecrypt_next_algorithm.3 b/librecrypt_next_algorithm.3 new file mode 100644 index 0000000..ca8bdf3 --- /dev/null +++ b/librecrypt_next_algorithm.3 @@ -0,0 +1,96 @@ +.TH LIBRECRYPT_NEXT_ALGORITHM 3 LIBRECRYPT +.SH NAME +librecrypt_next_algorithm - Iterate over algorithms in a chained password hash string + +.SH SYNOPSIS +.nf +#include <librecrypt.h> +.PP +char *\fBlibrecrypt_next_algorithm\fP(char **\fIhash\fP); +.fi +.PP +Link with +.IR -lrecrypt . + +.SH DESCRIPTION +The +.BR librecrypt_next_algorithm () +function is called repeatedly to extract each hash +algorithm (including its parameters) that shall be +chained together according to a provided hash string. +.PP +On the initial call, +.I *hash +shall point to the password hash string. +On each call the function updates +.I *hash +to its current parsing state. On each call except +the final one, the string is modified by replacing +the next instance of +.I LIBRECRYPT_ALGORITHM_LINK_DELIMITER +(which is +.BR \(aq>\(aq ) +with a null byte. +.PP +Each call returns the next algorithm substring. +Once all algorithms have been extracted and returned, +.IR NULL +is returned. +.PP +Except once the function has returned +.IR NULL , +overwriting the terminating null byte in the previously +returned substring +with +.I LIBRECRYPT_ALGORITHM_LINK_DELIMITER +restores the original hash string. +.PP +Unless already stripped out before input, the last +returned substring ends with the resulting hash. +.PP +.I hash +must not be +.IR NULL . + +.SH RETURN VALUES +The +.BR librecrypt_next_algorithm () +function returns a pointer to the next algorithm +substring, or +.IR NULL +when no more algorithms remain. + +.SH ERRORS +The +.BR librecrypt_next_algorithm () +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_next_algorithm () +T} Thread safety MT-Safe +T{ +.BR librecrypt_next_algorithm () +T} Async-signal safety AS-Safe +.TE +.sp + +.SH HISTORY +The +.BR librecrypt_next_algorithm () +function was introduced in version 1.0 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_chain_length (3), +.BR librecrypt_decompose_chain (3), +.BR librecrypt_decompose_chain1 (3) diff --git a/librecrypt_next_algorithm.c b/librecrypt_next_algorithm.c new file mode 100644 index 0000000..b825582 --- /dev/null +++ b/librecrypt_next_algorithm.c @@ -0,0 +1,82 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +extern inline char *librecrypt_next_algorithm(char **hash); + + +#else + + +static void +testcase_1(void) +{ + char hash[] = ">a$b>c$d>e$f$"; + char *s = hash, *a; + + EXPECT((a = librecrypt_next_algorithm(&s))); + EXPECT(s != NULL); + EXPECT(!strcmp(a, "")); + EXPECT(!strcmp(s, "a$b>c$d>e$f$")); + + EXPECT((a = librecrypt_next_algorithm(&s))); + EXPECT(s != NULL); + EXPECT(!strcmp(a, "a$b")); + EXPECT(!strcmp(s, "c$d>e$f$")); + + EXPECT((a = librecrypt_next_algorithm(&s))); + EXPECT(s != NULL); + EXPECT(!strcmp(a, "c$d")); + EXPECT(!strcmp(s, "e$f$")); + + EXPECT((a = librecrypt_next_algorithm(&s))); + EXPECT(s == NULL); + EXPECT(!strcmp(a, "e$f$")); + + EXPECT(librecrypt_next_algorithm(&s) == NULL); + EXPECT(s == NULL); +} + + +static void +testcase_2(void) +{ + char hash[] = "a$b>c$d>e$f$>"; + char *s = hash, *a; + + EXPECT((a = librecrypt_next_algorithm(&s))); + EXPECT(s != NULL); + EXPECT(!strcmp(a, "a$b")); + EXPECT(!strcmp(s, "c$d>e$f$>")); + + EXPECT((a = librecrypt_next_algorithm(&s))); + EXPECT(s != NULL); + EXPECT(!strcmp(a, "c$d")); + EXPECT(!strcmp(s, "e$f$>")); + + EXPECT((a = librecrypt_next_algorithm(&s))); + EXPECT(s != NULL); + EXPECT(!strcmp(a, "e$f$")); + EXPECT(!strcmp(s, "")); + + EXPECT((a = librecrypt_next_algorithm(&s))); + EXPECT(s == NULL); + EXPECT(!strcmp(a, "")); + + EXPECT(librecrypt_next_algorithm(&s) == NULL); + EXPECT(s == NULL); +} + + +int +main(void) +{ + SET_UP_ALARM(); + testcase_1(); + testcase_2(); + return 0; +} + + +#endif diff --git a/librecrypt_realise_salts.3 b/librecrypt_realise_salts.3 new file mode 100644 index 0000000..8a743cc --- /dev/null +++ b/librecrypt_realise_salts.3 @@ -0,0 +1,161 @@ +.TH LIBRECRYPT_REALISE_SALTS 3 LIBRECRYPT +.SH NAME +librecrypt_realise_salts - Realise asterisk-encoded random salts in a settings string + +.SH SYNOPSIS +.nf +#include <librecrypt.h> + +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); +.fi +.PP +Link with +.IR -lrecrypt . +Static linking may require additional flags +depending on enabled hash algorithms. + +.SH DESCRIPTION +The +.BR librecrypt_realise_salts () +function locates all asterisks followed by a +non-negative decimal number in +.IR settings +and replaces each with ASCII-encoded random +bytes (as many bytes as the number specifies), +writing the result to +.IR out_buffer . +.PP +.IR *rng , +or a default implementation (which tries to use +cryptographic random number generation but has +a non-cryptographic random number generation as +a last resort) if +.I rng +is +.IR NULL , +used to generate random bytes. +.I *rng +shall fill out with at least 1, and up to +.IR n , +random bytes, and return the number of bytes +written, or return -1 on failure. +.PP +The +.I data +argument is passed into the +.IR *rng -parameter +with the same name, as is and may be used by +.I *rng +for user-defined purposes. +.PP +If +.I rng +is +.IR NULL , +a default cryptographic random number generator +is used. The +.I *rng +function shall generate between 1 and +.I n +bytes (inclusively), write them to the front of +.IR out , +and return the number of generated bytes, or +-1 on failure. +.PP +On successful completion, if +.I size +is positive, the output is +null byte-terminated even if truncated. +.PP +If +.I rng +is +.IR NULL , +any encountered +.BR EINTR +is ignored; if it is encountered, +.I errno +will be set to +.BR EINTR , +but is otherwise left unmodified unless the +function fails due to one of the +errors listed in the +.B ERRORS +section. +.PP +.I settings +must not be +.IR NULL . + +.SH RETURN VALUES +The +.BR librecrypt_realise_salts () +function returns the number of bytes that +would have been written to +.IR out_buffer +if +.I size +was sufficiently large, or -1 on failure. + +.SH ERRORS +The +.BR librecrypt_realise_salts () +function will fail if: +.TP +.B ERANGE +The expected return value is greater than {SSIZE_MAX}. +.TP +.B EINVAL +Asterisk-encoding was used in an invalid context. +.TP +.B ENOSYS +.I settings +contain an algorithm that is not recognised or was +disabled at compile-time. +.PP +Additionally, unless +.I rng +is +.IR NULL , +.I *rng +may set +.IR errno . + +.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_realise_salts () +T} Thread safety MT-Safe +T{ +.BR librecrypt_realise_salts () +T} Async-signal safety T{ +AS-Safe /\fIrng\fP +.br +AS-Unsafe /\fI!rng\fP +T} +.TE +.sp +.PP +.I rng +is +.RI non- NULL , +.I *rng +may weaken the safety attributes. + +.SH HISTORY +The +.BR librecrypt_realise_salts () +function was introduced in version 1.0 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_make_settings (3) diff --git a/librecrypt_realise_salts.c b/librecrypt_realise_salts.c new file mode 100644 index 0000000..a4204a6 --- /dev/null +++ b/librecrypt_realise_salts.c @@ -0,0 +1,151 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +ssize_t +librecrypt_realise_salts(char *restrict out_buffer, size_t size, const char *settings, + ssize_t (*rng)(void *out, size_t n, void *user), void *user) +{ + const char *lut; + char pad; + int strict_pad, has_asterisk, nul_term = 0; + size_t i, j, n, min, ret = 0u; + size_t count, digit, q, r, left, mid, right; + + if (size) { + nul_term = 1; + size -= 1u; + } + + for (;;) { + for (; *settings == LIBRECRYPT_ALGORITHM_LINK_DELIMITER; settings++) { + if (size) { + *out_buffer++ = LIBRECRYPT_ALGORITHM_LINK_DELIMITER; + size -= 1u; + } + ret += 1u; + } + + if (!*settings) + break; + + has_asterisk = 0; + for (n = 0u; settings[n] && settings[n] != LIBRECRYPT_ALGORITHM_LINK_DELIMITER; n++) + if (settings[n] == '*') + has_asterisk = 1; + + if (!has_asterisk) { + min = size < n ? size : n; + memcpy(out_buffer, settings, min); + out_buffer += min; + settings = &settings[n]; + ret += n; + continue; + } + + if (*settings == '*') { + errno = EINVAL; + return -1; + } + + lut = librecrypt_get_encoding(settings, n, &pad, &strict_pad, 0); + if (!lut) + return -1; + pad = strict_pad ? pad : '\0'; + + for (i = 0; i < n;) { + if (settings[i] != '*') { + if (size) { + *out_buffer++ = settings[i]; + size -= 1u; + } + ret += 1u; + i++; + continue; + } + + if (++i == n) { + if (size) { + *out_buffer++ = '*'; + size -= 1u; + } + ret += 1u; + break; + } + if ('0' > settings[i] || settings[i] > '9') { + if (size) { + *out_buffer++ = '*'; + size -= 1u; + } + ret += 1u; + continue; + } + + count = 0; + for (; i < n && '0' <= settings[i] && settings[i] <= '9'; i++) { + digit = (size_t)(settings[i] - '0'); + if (count > ((size_t)SSIZE_MAX - digit) / 10u) + goto erange; + count *= 10u; + count += digit; + } + + q = count / 3u; + r = count % 3u; + mid = r ? r + 1u : 0u; + right = (!r ? 0u : pad ? 4u : r + 1u); + if (q > ((size_t)SSIZE_MAX - right) / 4u) + goto erange; + left = q * 4u; + ret += left + mid + right; + + left += mid; + if (left > size) { + left = size; + mid = 0; + } + if (librecrypt_fill_with_random_(out_buffer, left, rng, user)) + return -1; + for (j = 0; j < left; j++) + out_buffer[j] = lut[((unsigned char *)out_buffer)[j]]; + if (mid) + out_buffer[j] = lut[((unsigned char *)out_buffer)[j] & (r == 1u ? ~15u : ~3u)]; + out_buffer = &out_buffer[left]; + size -= left; + + if (right > size) + right = size; + for (j = 0; j < right; j++) + out_buffer[right] = pad; + out_buffer = &out_buffer[right]; + size -= right; + } + } + + if (nul_term) + *out_buffer = '\0'; + if (ret > (size_t)SSIZE_MAX) + goto erange; + return (ssize_t)ret; + +erange: + errno = ERANGE; + return -1; +} + + +#else + + +int +main(void) +{ + SET_UP_ALARM(); + + return 0; +} + + +#endif +/* TODO test */ diff --git a/librecrypt_rng_.c b/librecrypt_rng_.c new file mode 100644 index 0000000..8b9f138 --- /dev/null +++ b/librecrypt_rng_.c @@ -0,0 +1,146 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +ssize_t +librecrypt_rng_(void *out, size_t n, void *user) +{ + static unsigned int seed = 0u; + unsigned char *buf = out; + int v, fd, saved_errno = errno; + unsigned int state; + size_t i, min, ret = 0u; + ssize_t r; + struct timespec ts; +#if defined(__linux__) && defined(AT_RANDOM) + const unsigned char *at_random; + uintptr_t at_random_addr; +#endif + + (void) user; + + if (!n) + return 0; + + if (n > (size_t)SSIZE_MAX) + n = (size_t)SSIZE_MAX; + +#if defined(__linux__) + for (;;) { + r = getrandom(buf, n, 0u); + if (r < 0) { + if (errno != EINTR) + break; + saved_errno = EINTR; + } else if (r > 0) { + ret += (size_t)r; + buf = &buf[(size_t)r]; + n -= (size_t)r; + if (!n) + goto out; + } else { + break; + } + } +#endif + + for (;;) { + min = n < 256u ? n : 256u; + if (getentropy(buf, min)) { + if (errno != EINTR) + break; + saved_errno = EINTR; + } else { + ret += min; + buf = &buf[min]; + n -= min; + if (!n) + goto out; + } + } + +#ifndef O_CLOEXEC +# define O_CLOEXEC 0 +#endif + fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); + if (fd >= 0) { + for (;;) { + r = read(fd, buf, n); + if (r < 0) { + if (errno != EINTR) { + close(fd); + break; + } + saved_errno = EINTR; + } else if (r > 0) { + ret += (size_t)r; + buf = &buf[(size_t)r]; + n -= (size_t)r; + if (!n) { + close(fd); + goto out; + } + } else { + close(fd); + break; + } + } + } + + state = seed; + if (!state) { + state = (unsigned int)rand(); + state ^= (unsigned int)time(NULL); + if (!clock_gettime(CLOCK_MONOTONIC, &ts)) { + state ^= (unsigned int)ts.tv_sec; + state ^= (unsigned int)ts.tv_nsec; + } +#if defined(__linux__) && defined(AT_RANDOM) + at_random_addr = (uintptr_t)getauxval(AT_RANDOM); + if (at_random_addr) { + at_random = (const void *)at_random_addr; + for (i = 0u; i < 16u; i++) + state ^= (unsigned int)at_random[i] << (i % sizeof(unsigned int) * 8u); + } + } +#endif + for (i = 0u; i < n; i++) { + v = rand_r(&state); + v = (int)((uintmax_t)v * 255u / (uintmax_t)RAND_MAX); + buf[i] = (unsigned char)v; + } + ret += n; + seed = state; + +out: + errno = saved_errno; + return (ssize_t)ret; +} + + +#else + + +int +main(void) +{ + /* TODO test failure cases */ + + unsigned char buf1[1024u]; + unsigned char buf2[sizeof(buf1)]; + ssize_t n1, n2; + + SET_UP_ALARM(); + + n1 = librecrypt_rng_(buf1, sizeof(buf1), NULL); + n2 = librecrypt_rng_(buf2, sizeof(buf2), NULL); + EXPECT(n1 >= 128 && (size_t)n1 <= sizeof(buf1)); + EXPECT(n2 >= 128 && (size_t)n2 <= sizeof(buf2)); + EXPECT(memcmp(buf1, buf2, (size_t)(n1 < n2 ? n1 : n2))); + + return 0; +} + + +#endif diff --git a/librecrypt_settings_prefix.3 b/librecrypt_settings_prefix.3 new file mode 100644 index 0000000..622af0e --- /dev/null +++ b/librecrypt_settings_prefix.3 @@ -0,0 +1,80 @@ +.TH LIBRECRYPT_SETTINGS_PREFIX 3 LIBRECRYPT +.SH NAME +librecrypt_settings_prefix - Get length of settings prefix in a password hash string + +.SH SYNOPSIS +.nf +#include <librecrypt.h> + +size_t \fBlibrecrypt_settings_prefix\fP(const char *\fIhash\fP); +.fi +.PP +Link with +.IR -lrecrypt . + +.SH DESCRIPTION +The +.BR librecrypt_settings_prefix () +function returns the number of bytes, from the front of +.IR hash , +that make up the algorithm configuration. +.PP +The string is scanned for the delimiters +.I LIBRECRYPT_HASH_COMPOSITION_DELIMITER +(which is +.BR \(aq$\(aq ) +and +.I LIBRECRYPT_ALGORITHM_LINK_DELIMITER +(which is +.BR \(aq>\(aq ), +and the returned value is one byte past the +last occurrence, or 0 if none was found. +.PP +.I hash +must not be +.IR NULL . + +.SH RETURN VALUES +The +.BR librecrypt_settings_prefix () +function returns the number of bytes that make up the +settings prefix. The return value may be 0, which shall +be treated as any other valid return value. +.PP +.IR &hash[r] , +where +.I r +is the return value, points to the hash result proper. + +.SH ERRORS +The +.BR librecrypt_settings_prefix () +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_settings_prefix () +T} Thread safety MT-Safe +T{ +.BR librecrypt_settings_prefix () +T} Async-signal safety AS-Safe +.TE +.sp + +.SH HISTORY +The +.BR librecrypt_settings_prefix () +function was introduced in version 1.0 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_chain_length (3) diff --git a/librecrypt_settings_prefix.c b/librecrypt_settings_prefix.c new file mode 100644 index 0000000..edc7222 --- /dev/null +++ b/librecrypt_settings_prefix.c @@ -0,0 +1,34 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +extern inline size_t librecrypt_settings_prefix(const char *hash); + + +#else + + +#define CHECK(PREFIX, SUFFIX)\ + do {\ + EXPECT(librecrypt_settings_prefix(PREFIX SUFFIX) == sizeof(PREFIX) - 1u);\ + EXPECT(librecrypt_settings_prefix(PREFIX) == sizeof(PREFIX) - 1u);\ + } while (0) + + +int +main(void) +{ + SET_UP_ALARM(); + CHECK("", "result"); + CHECK(">", "double-des result"); + CHECK("$", "x"); + CHECK("y$", "x"); + CHECK("y>", "x"); + CHECK("a$b$c>d$e$", "x"); + CHECK("a$b$c>", "x"); + return 0; +} + + +#endif diff --git a/librecrypt_test_supported.3 b/librecrypt_test_supported.3 new file mode 100644 index 0000000..eb51e2d --- /dev/null +++ b/librecrypt_test_supported.3 @@ -0,0 +1,98 @@ +.TH LIBRECRYPT_TEST_SUPPORTED 3 LIBRECRYPT +.SH NAME +librecrypt_test_supported - Check whether an algorithm chain is supported + +.SH SYNOPSIS +.nf +#include <librecrypt.h> + +int \fBlibrecrypt_test_supported\fP(const char *\fIphrase\fP, size_t \fIlen\fP, int \fItext\fP, const char *\fIsettings\fP); +.fi +.PP +Link with +.IR -lrecrypt . +Static linking may require additional flags +depending on enabled hash algorithms. + +.SH DESCRIPTION +The +.BR librecrypt_test_supported () +function checks whether a hash algorithm chain is +supported for the given input, and that each +algorithm configuration is valid. +.PP +The +.I settings +argument is a password hash string. Algorithm +tuning parameters and the hash result may be omitted. +.PP +The +.I phrase +argument is the password to hash. +It may contain null bytes and may be +.IR NULL +even if +.I len +is non-zero. If +.I phrase +is +.IR NULL , +the function checks whether the specified length +is supported and assumes an arbitrary byte +sequence if +.I text +is zero. If +.I text +is non-zero, UTF-8 without null bytes is assumed. +.PP +If +.I phrase +is not +.IR NULL , +.I text +is ignored. +.PP +.I settings +must not be +.IR NULL . + +.SH RETURN VALUES +The +.BR librecrypt_test_supported () +function returns 1 if the configuration is supported, +and 0 otherwise. + +.SH ERRORS +The +.BR librecrypt_test_supported () +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_test_supported () +T} Thread safety MT-Safe +T{ +.BR librecrypt_test_supported () +T} Async-signal safety AS-Safe +.TE +.sp + +.SH HISTORY +The +.BR librecrypt_test_supported () +function was introduced in version 1.0 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_hash_binary (3), +.BR librecrypt_hash (3), +.BR librecrypt_crypt (3) diff --git a/librecrypt_test_supported.c b/librecrypt_test_supported.c new file mode 100644 index 0000000..fad8bd9 --- /dev/null +++ b/librecrypt_test_supported.c @@ -0,0 +1,59 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +librecrypt_test_supported(const char *phrase, size_t len, int text, const char *settings) +{ + const struct algorithm *algo; + size_t n, prefix; + int has_next; + + for (;;) { + has_next = 0; + for (n = 0u; settings[n]; n++) { + if (settings[n] == LIBRECRYPT_ALGORITHM_LINK_DELIMITER) { + has_next = 1; + break; + } + } + + algo = librecrypt_find_first_algorithm_(settings, n); + if (!algo) + return 0; + + prefix = (*algo->get_prefix)(settings, n); + if (has_next && prefix < n) + return 0; + + if (!(*algo->test_supported)(phrase, len, text, settings, prefix)) + return 0; + + if (!has_next) + return 1; + + phrase = NULL; + len = algo->hash_size; + text = 0; + + settings = &settings[n]; + settings++; + } +} + + +#else + + +int +main(void) +{ + SET_UP_ALARM(); + + return 0; +} + + +#endif +/* TODO test */ diff --git a/librecrypt_wipe.3 b/librecrypt_wipe.3 new file mode 100644 index 0000000..48f5829 --- /dev/null +++ b/librecrypt_wipe.3 @@ -0,0 +1,57 @@ +.TH LIBRECRYPT_WIPE 3 LIBRECRYPT +.SH NAME +librecrypt_wipe - Securely erase a memory buffer + +.SH SYNOPSIS +.nf +#include <librecrypt.h> + +void \fBlibrecrypt_wipe\fP(void *\fIbuffer\fP, size_t \fIsize\fP); +.fi +.PP +Link with +.IR -lrecrypt . + +.SH DESCRIPTION +The +.BR librecrypt_wipe () +function securely erases +.I size +bytes from +.IR buffer . + +.SH RETURN VALUES +None. + +.SH ERRORS +The +.BR librecrypt_wipe () +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_wipe () +T} Thread safety MT-Safe +T{ +.BR librecrypt_wipe () +T} Async-signal safety AS-Safe +.TE +.sp + +.SH HISTORY +The +.BR librecrypt_wipe () +function was introduced in version 1.0 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_wipe_str (3) diff --git a/librecrypt_wipe.c b/librecrypt_wipe.c new file mode 100644 index 0000000..a704ef0 --- /dev/null +++ b/librecrypt_wipe.c @@ -0,0 +1,65 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +#if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wsuggest-attribute=const" +# pragma GCC diagnostic ignored "-Wsuggest-attribute=pure" +#endif + + +void *(*volatile librecrypt_explicit_memset_____)(void *, int, size_t) = &memset; + + +#if defined(__clang__) /* before __GNUC__ because that is also set in clang */ +# if __has_attribute(optnone) +__attribute__((optnone)) +# endif +#elif defined(__GNUC__) +# if __GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ >= 40400 +__attribute__((optimize("-O0"))) +# endif +#endif +void +librecrypt_wipe(void *buffer, size_t size) +{ + if (buffer && size) { + buffer = (*librecrypt_explicit_memset_____)(buffer, 0, size); +#if defined(__GNUC__) + __asm__ volatile ("" :: "g" (buffer) : "memory"); +#else + (void) buffer; +#endif + } +} + + +#else + + +int +main(void) +{ + char *buf; + + SET_UP_ALARM(); + +#if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wnonnull" +#endif + + librecrypt_wipe(NULL, 0u); + librecrypt_wipe(NULL, 4094u); + librecrypt_wipe((void *)(intptr_t)-1, 0u); + + buf = malloc(256u); + memset(buf, 99, 256u); + librecrypt_wipe(buf, 256u); + free(buf); /* TODO should test memory is wiped */ + + return 0; +} + + +#endif diff --git a/librecrypt_wipe_str.3 b/librecrypt_wipe_str.3 new file mode 100644 index 0000000..80aa301 --- /dev/null +++ b/librecrypt_wipe_str.3 @@ -0,0 +1,61 @@ +.TH LIBRECRYPT_WIPE_STR 3 LIBRECRYPT +.SH NAME +librecrypt_wipe_str - Securely erase a string + +.SH SYNOPSIS +.nf +#include <librecrypt.h> + +void \fBlibrecrypt_wipe_str\fP(char *\fIstring\fP); +.fi +.PP +Link with +.IR -lrecrypt . + +.SH DESCRIPTION +The +.BR librecrypt_wipe_str () +function securely erases the string referenced by +.IR string . +.PP +If +.I string +is +.IR NULL , +no action is taken. + +.SH RETURN VALUES +None. + +.SH ERRORS +The +.BR librecrypt_wipe_str () +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_wipe_str () +T} Thread safety MT-Safe +T{ +.BR librecrypt_wipe_str () +T} Async-signal safety AS-Safe +.TE +.sp + +.SH HISTORY +The +.BR librecrypt_wipe_str () +function was introduced in version 1.0 of +.BR librecrypt . + +.SH SEE ALSO +.BR librecrypt (7), +.BR librecrypt_wipe (3) diff --git a/librecrypt_wipe_str.c b/librecrypt_wipe_str.c new file mode 100644 index 0000000..8c31cdc --- /dev/null +++ b/librecrypt_wipe_str.c @@ -0,0 +1,39 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +extern inline void librecrypt_wipe_str(char *string); + + +#else + + +#define CHECK(TEXT)\ + do {\ + memset(buf, 0, sizeof(buf));\ + stpcpy(buf, (TEXT));\ + assert(!strcmp(buf, (TEXT)));\ + librecrypt_wipe_str(buf);\ + for (i = 0u; i < sizeof(buf); i++)\ + EXPECT(!buf[i]);\ + } while (0) + + +int +main(void) +{ + char buf[64u]; + size_t i; + SET_UP_ALARM(); + librecrypt_wipe_str(NULL); + CHECK(""); + CHECK("hello"); + CHECK("hello developer"); + CHECK(" hello developer "); + CHECK("\1 hello developer \1"); + return 0; +} + + +#endif diff --git a/mk/linux.mk b/mk/linux.mk new file mode 100644 index 0000000..ad58f69 --- /dev/null +++ b/mk/linux.mk @@ -0,0 +1,6 @@ +LIBEXT = so +LIBFLAGS = -shared -Wl,-soname,lib$(LIB_NAME).$(LIBEXT).$(LIB_MAJOR) +LIBMAJOREXT = $(LIBEXT).$(LIB_MAJOR) +LIBMINOREXT = $(LIBEXT).$(LIB_VERSION) + +FIX_INSTALL_NAME = : diff --git a/mk/macos.mk b/mk/macos.mk new file mode 100644 index 0000000..1b172e6 --- /dev/null +++ b/mk/macos.mk @@ -0,0 +1,6 @@ +LIBEXT = dylib +LIBFLAGS = -dynamiclib -Wl,-compatibility_version,$(LIB_MAJOR) -Wl,-current_version,$(LIB_VERSION) +LIBMAJOREXT = $(LIB_MAJOR).$(LIBEXT) +LIBMINOREXT = $(LIB_VERSION).$(LIBEXT) + +FIX_INSTALL_NAME = install_name_tool -id "$(PREFIX)/lib/librecrypt.$(LIBMAJOREXT)" diff --git a/mk/windows.mk b/mk/windows.mk new file mode 100644 index 0000000..ed5ec8d --- /dev/null +++ b/mk/windows.mk @@ -0,0 +1,6 @@ +LIBEXT = dll +LIBFLAGS = -shared +LIBMAJOREXT = $(LIB_MAJOR).$(LIBEXT) +LIBMINOREXT = $(LIB_VERSION).$(LIBEXT) + +FIX_INSTALL_NAME = : |
