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