aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Makefile7
-rw-r--r--common.h144
-rw-r--r--librecrypt_base64_len_to_raw_len_.c49
-rw-r--r--librecrypt_concat_char_no_nul_.c25
-rw-r--r--librecrypt_hash_.c97
-rw-r--r--librecrypt_is_base64_properly_padded_.c63
-rw-r--r--librecrypt_post_concat_adjust_.c25
-rw-r--r--librecrypt_raw_len_to_base64_len_.c72
8 files changed, 414 insertions, 68 deletions
diff --git a/Makefile b/Makefile
index 33e9c8a..55918d4 100644
--- a/Makefile
+++ b/Makefile
@@ -68,10 +68,15 @@ OBJ_PRIVATE =\
librecrypt_fill_with_random_.o\
librecrypt_find_first_algorithm_.o\
librecrypt_get_pepper_.o\
+ librecrypt_concat_void_.o\
librecrypt_concat_mem_.o\
librecrypt_concat_str_.o\
librecrypt_concat_uint_.o\
- librecrypt_concat_void_.o\
+ librecrypt_concat_char_no_nul_.o\
+ librecrypt_post_concat_adjust_.o\
+ librecrypt_raw_len_to_base64_len_.o\
+ librecrypt_base64_len_to_raw_len_.o\
+ librecrypt_is_base64_properly_padded_.o\
$(OBJ_COMMON_RFC4848S4)
USE_OBJ_COMMON_RFC4848S4 =\
diff --git a/common.h b/common.h
index 1021516..c1b21e8 100644
--- a/common.h
+++ b/common.h
@@ -452,7 +452,7 @@ librecrypt_concat_mem_(struct concat_state *state, const char *text, size_t len)
* @param state The buffer to write to, see `struct concat_state` for more information
* @param text The text to copy to the buffer
*/
-LIBRECRYPT_NONNULL_1__ LIBRECRYPT_READ_STR__(2) HIDDEN
+LIBRECRYPT_NONNULL__ LIBRECRYPT_READ_STR__(2) HIDDEN
inline void
librecrypt_concat_str_(struct concat_state *state, const char *text)
{
@@ -477,6 +477,148 @@ LIBRECRYPT_NONNULL_1__ HIDDEN
void librecrypt_concat_uint_(struct concat_state *state, uintmax_t value);
+/**
+ * Concatenate a single chracter onto a string
+ *
+ * NB! NUL termination will not be added
+ *
+ * Truncation is done to ensure the resulting string
+ * does not overrun it's buffer but is still has room
+ * for NUL terminnation (assuming the buffer is not
+ * zero-sized)
+ *
+ * @param state The buffer to write to, see `struct concat_state` for more information
+ * @param c The character to append
+ */
+LIBRECRYPT_NONNULL_1__ HIDDEN
+inline void
+librecrypt_concat_char_no_nul_(struct concat_state *state, char c)
+{
+ if (state->len == SIZE_MAX)
+ abort(); /* TODO not covered */
+ state->len += 1u;
+ if (state->size > 1u) {
+ *state->buf++ = c;
+ state->size -= 1u;
+ }
+}
+
+
+/**
+ * Adjust a `struct concat_state` has been modified by
+ * another function
+ *
+ * This function will not by it self modify the buffer
+ * in any way. It will not add NUL-termination. This
+ * function will only update `*state` to reflect that
+ * some function wanted to write `len` bytes (but may
+ * have written less due to the buffer being to small).
+ *
+ * @param state The buffer to write to, see `struct concat_state` for more information
+ * @param len the length of the concatenated data sans NUL-termination
+ * @return Normally 0, -1 if `state->len` would overflow
+ *
+ * The proper error code when this function returns -1 is
+ * usually EOVERFLOW, however this function does not modify errno
+ *
+ * It is unspecified whether `state->buf` and `state->size` will
+ * be updated when -1 is returned. It is also unspecified whether
+ * `state->len` will be updated when -1 is returned.
+ *
+ * Precondition: `state->buf != NULL`
+ */
+LIBRECRYPT_NONNULL_1__ LIBRECRYPT_WUR__ HIDDEN
+inline int
+librecrypt_post_concat_adjust_(struct concat_state *state, size_t len)
+{
+ size_t n = state->size ? MIN(state->size - 1u, len) : 0u;
+ state->buf = &state->buf[n];
+ state->size -= n;
+ if (state->len > SIZE_MAX - len)
+ return -1;
+ state->len += len;
+ return 0;
+}
+
+
+/**
+ * Get the base64-encoding length for some size of data
+ *
+ * @param raw_len The size of the data
+ * @param padded Whether the base64-encoding shall be padded to a multiple of 4 bytes
+ * @param ascii_len_out Output parameter for the length of the base64-encoding
+ * @return Normally 1, 0 if the value for `*ascii_len_out` could not be represented
+ *
+ * The proper error code when this function returns 0 is
+ * usually EOVERFLOW, however this function does not modify errno
+ */
+LIBRECRYPT_WUR__ LIBRECRYPT_NONNULL__ HIDDEN
+inline int
+librecrypt_raw_len_to_base64_len_(size_t raw_len, int padded, size_t *ascii_len_out)
+{
+ size_t q = raw_len / 3u;
+ size_t r = raw_len % 3u;
+ if (r) {
+ if (padded)
+ r = 4u; /* padding to for bytes */
+ else
+ r += 1u; /* 3n+m bytes: 4n+m+1 chars, unless m=0 */
+ }
+ if (q > (SIZE_MAX - r) / 4u)
+ return 0;
+ *ascii_len_out = q * 4u + r;
+ return 1;
+}
+
+
+/**
+ * Get the size of some data based on the length of its base64-encoding
+ *
+ * @param ascii_len The length of the data's unpadded base64-encoding
+ * @param raw_len_out Output parameter for the size of the data
+ * @return Normally 1, 0 if `ascii_len` doesn't have proper
+ * for a base64-encoding
+ *
+ * The proper error code when this function returns 0 is
+ * usually EINVAL, however this function does not modify errno
+ */
+LIBRECRYPT_WUR__ LIBRECRYPT_NONNULL__ HIDDEN
+inline int
+librecrypt_base64_len_to_raw_len_(size_t ascii_len, size_t *raw_len_out)
+{
+ size_t q = ascii_len / 4u;
+ size_t r = ascii_len % 4u;
+ if (r == 1u)
+ return 0;
+ *raw_len_out = q * 3u;
+ if (r)
+ *raw_len_out += r - 1u;
+ return 1;
+}
+
+
+/**
+ * Check whether a base64-encoding is padded to a multiple
+ * of 4 characters, without being excessively padded
+ *
+ * @param unpadded_len The length of the base64-encoding sans it's padding
+ * @param padded_len The length of the base64-encoding including any present padding
+ * @return Whether the base64-encoding is appropriately padded
+ *
+ * Precondition: `padded_len >= unpadded_len`
+ * Precondition: librecrypt_base64_len_to_raw_len_(unpadded_len, _) != 0 (i.e. `unpadded_len % 4u != 1u`)
+ */
+LIBRECRYPT_WUR__ CONST HIDDEN
+inline int
+librecrypt_is_base64_properly_padded_(size_t unpadded_len, size_t padded_len)
+{
+ if (padded_len % 4u)
+ return 0;
+ if (padded_len - unpadded_len >= 4u)
+ return 0;
+ return 1;
+}
+
#ifdef TEST
# include "libtest/libtest.h"
diff --git a/librecrypt_base64_len_to_raw_len_.c b/librecrypt_base64_len_to_raw_len_.c
new file mode 100644
index 0000000..a540278
--- /dev/null
+++ b/librecrypt_base64_len_to_raw_len_.c
@@ -0,0 +1,49 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+extern inline int librecrypt_base64_len_to_raw_len_(size_t ascii_len, size_t *raw_len_out);
+
+
+#else
+
+
+#define CHECK_GOOD(IN, OUT)\
+ do {\
+ size_t out = SIZE_MAX;\
+ assert(librecrypt_base64_len_to_raw_len_((IN), &out) == 1);\
+ assert(out == (OUT));\
+ } while (0)
+
+
+#define CHECK_BAD(IN)\
+ do {\
+ size_t out;\
+ assert(librecrypt_base64_len_to_raw_len_((IN), &out) == 0);\
+ } while (0)
+
+
+int
+main(void)
+{
+ size_t i, in_off = 0u, out_off = 0u;
+
+ SET_UP_ALARM();
+ INIT_RESOURCE_TEST();
+
+ for (i = 0u; i < 8u; i++) {
+ CHECK_GOOD(in_off + 0u, out_off + 0u);
+ CHECK_BAD(in_off + 1u);
+ CHECK_GOOD(in_off + 2u, out_off + 1u);
+ CHECK_GOOD(in_off + 3u, out_off + 2u);
+ in_off += 4u;
+ out_off += 3u;
+ }
+
+ STOP_RESOURCE_TEST();
+ return 0;
+}
+
+
+#endif
diff --git a/librecrypt_concat_char_no_nul_.c b/librecrypt_concat_char_no_nul_.c
new file mode 100644
index 0000000..421509c
--- /dev/null
+++ b/librecrypt_concat_char_no_nul_.c
@@ -0,0 +1,25 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+extern inline void librecrypt_concat_char_no_nul_(struct concat_state *state, char c);
+
+
+#else
+
+
+int
+main(void)
+{
+ SET_UP_ALARM();
+ INIT_RESOURCE_TEST();
+
+ /* TODO test */
+
+ STOP_RESOURCE_TEST();
+ return 0;
+}
+
+
+#endif
diff --git a/librecrypt_hash_.c b/librecrypt_hash_.c
index fcaa795..3f3bfea 100644
--- a/librecrypt_hash_.c
+++ b/librecrypt_hash_.c
@@ -46,18 +46,15 @@ ssize_t
librecrypt_hash_(char *restrict out_buffer, size_t size, const char *phrase, size_t len,
const char *settings, LIBRECRYPT_CONTEXT *ctx, enum action action)
{
- /* TODO rewrite to use the string building functions */
-
+ struct concat_state concat_state = {out_buffer, size, 0u};
const struct librecrypt_algorithm *algo;
ssize_t (*rng)(void *out, size_t n, void *user) = NULL;
char *settings_scratch = NULL;
char *phrase_scratches[2] = {NULL, NULL};
size_t phrase_scratch_sizes[2] = {0u, 0u};
- size_t i, n, ascii_len, min, prefix, ret = 0u;
- size_t hash_size, digit, quotient, remainder;
- int has_next, phrase_scratch_i = 0;
+ size_t i, n, ascii_len, prefix, hash_size, digit;
+ int r, saved_errno, has_next, phrase_scratch_i = 0;
ssize_t r_len;
- int r, saved_errno;
void *new;
/* Realise asterisk-encoded salts */
@@ -74,11 +71,11 @@ librecrypt_hash_(char *restrict out_buffer, size_t size, const char *phrase, siz
/* If there no output, don't waste time and entropy
* generating random salts, just write generates
* zeroes instead */
- if (!size)
+ if (!concat_state.size)
rng = &zero_generator;
/* Generate the salts */
- r_len = librecrypt_realise_salts(out_buffer, size, settings, rng, NULL, ctx);
+ r_len = librecrypt_realise_salts(NULL, 0u, settings, rng, NULL, ctx);
if (r_len < 0) {
if (errno == ERANGE) {
errno = ENOMEM;
@@ -154,20 +151,13 @@ next:
for (; i < n; i++)
if (settings[i] != algo->pad)
break;
- if ((i - prefix) % 4u)
- goto einval;
- if (i - prefix - hash_size >= 4u)
+ if (!librecrypt_is_base64_properly_padded_(hash_size, i - prefix))
goto einval;
}
if (i != n)
goto einval;
- quotient = hash_size / 4u;
- remainder = hash_size % 4u;
- if (remainder == 1u)
+ if (!librecrypt_base64_len_to_raw_len_(hash_size, &hash_size))
goto einval;
- hash_size = quotient * 3u;
- if (remainder)
- hash_size += remainder - 1u;
/* Must align with fixed hash size when hash size is fixed */
if (!algo->flexible_hash_size && hash_size != algo->hash_size)
goto einval;
@@ -179,18 +169,11 @@ next:
/* Include hash length specification */
prefix = n;
}
- min = size ? MIN(size - 1u, prefix) : 0u;
- size -= min;
- if (min)
- memcpy(out_buffer, settings, min);
- out_buffer = &out_buffer[min];
- if (ret > SIZE_MAX - prefix)
- abort(); /* $covered$ (impossible) */
- ret += prefix;
+ librecrypt_concat_mem_(&concat_state, settings, prefix);
}
/* Unless output is fully truncated, ensure scratch for intermediate hash is large enough */
- if (size && phrase_scratch_sizes[phrase_scratch_i] < hash_size) {
+ if (concat_state.size && phrase_scratch_sizes[phrase_scratch_i] < hash_size) {
librecrypt_wipe(phrase_scratches[phrase_scratch_i], phrase_scratch_sizes[phrase_scratch_i]);
new = realloc(phrase_scratches[phrase_scratch_i], hash_size);
if (!new) {
@@ -208,14 +191,14 @@ next:
if (has_next) {
/* Intermediate hash: write to scratch */
hash_to_scratch:
- r = (*algo->hash)(size ? phrase_scratches[phrase_scratch_i] : NULL,
- size ? phrase_scratch_sizes[phrase_scratch_i] : 0u,
+ r = (*algo->hash)(concat_state.size ? phrase_scratches[phrase_scratch_i] : NULL,
+ concat_state.size ? phrase_scratch_sizes[phrase_scratch_i] : 0u,
phrase, len, settings, n, ctx);
} else if (action == BINARY_HASH) {
/* Final hash in binary: write immediate to output */
hash_to_output:
- r = (*algo->hash)(out_buffer, size, phrase, len, settings, n, ctx);
- } else if (size < hash_size) {
+ r = (*algo->hash)(concat_state.buf, concat_state.size, phrase, len, settings, n, ctx);
+ } else if (concat_state.size < hash_size) {
/* Final hash in ASCII: write to scratch if output is truncated,
* because it will be converted to ASCII later */
goto hash_to_scratch;
@@ -233,42 +216,31 @@ next:
if (action == BINARY_HASH) {
/* Binary output: we already have the has in binary,
* so yes add the length to the return value */
- if (ret != 0u)
+ if (concat_state.len != 0u)
abort(); /* $covered$ (impossible) */
- ret += hash_size;
- } else if (!size) {
+ concat_state.len += hash_size;
+ } else if (!concat_state.size) {
/* ASCII hash but not output: just calculate the
* ASCII length and add it to the return value */
- ascii_len = hash_size % 3u;
- if (ascii_len) {
- if (algo->pad && algo->strict_pad)
- ascii_len = 4u; /* padding to for bytes */
- else
- ascii_len += 1u; /* 3n+m bytes: 4n+m+1 chars, unless m=0 */
- }
- if (hash_size / 3u > (SIZE_MAX - ascii_len) / 4u)
+ if (!librecrypt_raw_len_to_base64_len_(hash_size, algo->pad && algo->strict_pad, &ascii_len))
goto eoverflow; /* $covered$ (on 32-bit, impossible on wider) */
- ascii_len += hash_size / 3u * 4u;
goto include_ascii;
} else {
/* ASCII output: convert from binary to ASCII,
* and add ASCII length to the return value */
- ascii_len = librecrypt_encode(out_buffer, size,
- size < hash_size ? phrase_scratches[phrase_scratch_i] : out_buffer,
+ ascii_len = librecrypt_encode(concat_state.buf, concat_state.size,
+ concat_state.size < hash_size
+ ? phrase_scratches[phrase_scratch_i] : concat_state.buf,
hash_size, algo->encoding_lut, algo->strict_pad ? algo->pad : '\0');
/* SIZE_MAX could mean success, however we will
- * fail when convert `ret` from size_t to ssize_t,
- * so we can treat SIZE_MAX as failure even when
- * it's a success */
+ * fail when convert `concat_state.len` from size_t
+ * to ssize_t, so we can treat SIZE_MAX as failure
+ * even when it's a success */
if (ascii_len == SIZE_MAX)
goto eoverflow; /* $covered$ (manually) */
include_ascii:
- min = size ? MIN(size - 1u, ascii_len) : 0u;
- out_buffer = &out_buffer[min];
- size -= min;
- if (ret > SIZE_MAX - ascii_len)
+ if (librecrypt_post_concat_adjust_(&concat_state, ascii_len))
goto eoverflow; /* $covered$ (on 32-bit) */
- ret += ascii_len;
}
} else {
/* Intermediate hash: */
@@ -276,7 +248,7 @@ next:
/* Swap scratches, so that the intermediate output
* becomes the next algorithm's input, but use NULL if output is
* truncated (measure only) */
- phrase = size ? phrase_scratches[phrase_scratch_i] : NULL;
+ phrase = concat_state.size ? phrase_scratches[phrase_scratch_i] : NULL;
phrase_scratch_i ^= 1;
len = hash_size;
@@ -286,15 +258,8 @@ next:
settings++;
/* For `librecrypt_crypt`: add '>' to the password hash string */
- if (action == ASCII_CRYPT) {
- if (ret == SIZE_MAX)
- abort(); /* $covered$ (impossible) */
- ret += 1u;
- if (size > 1u) {
- *out_buffer++ = LIBRECRYPT_ALGORITHM_LINK_DELIMITER;
- size -= 1u;
- }
- }
+ if (action == ASCII_CRYPT)
+ librecrypt_concat_char_no_nul_(&concat_state, LIBRECRYPT_ALGORITHM_LINK_DELIMITER);
/* Calculate the hash, from the intermediate output */
goto next;
@@ -315,16 +280,16 @@ next:
}
/* NUL-terminate output if it is a string (`out_buffer` is offset at every write to it) */
- if (size && action != BINARY_HASH)
- out_buffer[0] = '\0';
+ if (concat_state.size && action != BINARY_HASH)
+ concat_state.buf[0] = '\0';
- if (ret > (size_t)SSIZE_MAX) {
+ if (concat_state.len > (size_t)SSIZE_MAX) {
/* $covered{$ (manually) */
errno = EOVERFLOW;
return -1;
/* $covered}$ */
}
- return (ssize_t)ret;
+ return (ssize_t)concat_state.len;
/* $covered{$ (since we have covered gotos to this label) */
eoverflow:
diff --git a/librecrypt_is_base64_properly_padded_.c b/librecrypt_is_base64_properly_padded_.c
new file mode 100644
index 0000000..0cf1c09
--- /dev/null
+++ b/librecrypt_is_base64_properly_padded_.c
@@ -0,0 +1,63 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+extern inline int librecrypt_is_base64_properly_padded_(size_t unpadded_len, size_t padded_len);
+
+
+#else
+
+
+int
+main(void)
+{
+ SET_UP_ALARM();
+ INIT_RESOURCE_TEST();
+
+ assert(librecrypt_is_base64_properly_padded_(0u, 0u) == 1);
+ assert(librecrypt_is_base64_properly_padded_(0u, 1u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(0u, 2u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(0u, 3u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(0u, 4u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(0u, 5u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(0u, 6u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(0u, 7u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(0u, 8u) == 0);
+
+ assert(librecrypt_is_base64_properly_padded_(2u, 2u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(2u, 3u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(2u, 4u) == 1);
+ assert(librecrypt_is_base64_properly_padded_(2u, 5u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(2u, 6u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(2u, 7u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(2u, 8u) == 0);
+
+ assert(librecrypt_is_base64_properly_padded_(3u, 3u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(3u, 4u) == 1);
+ assert(librecrypt_is_base64_properly_padded_(3u, 5u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(3u, 6u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(3u, 7u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(3u, 8u) == 0);
+
+ assert(librecrypt_is_base64_properly_padded_(4u, 4u) == 1);
+ assert(librecrypt_is_base64_properly_padded_(4u, 5u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(4u, 6u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(4u, 7u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(4u, 8u) == 0);
+
+ assert(librecrypt_is_base64_properly_padded_(6u, 6u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(6u, 7u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(6u, 8u) == 1);
+
+ assert(librecrypt_is_base64_properly_padded_(7u, 7u) == 0);
+ assert(librecrypt_is_base64_properly_padded_(7u, 8u) == 1);
+
+ assert(librecrypt_is_base64_properly_padded_(8u, 8u) == 1);
+
+ STOP_RESOURCE_TEST();
+ return 0;
+}
+
+
+#endif
diff --git a/librecrypt_post_concat_adjust_.c b/librecrypt_post_concat_adjust_.c
new file mode 100644
index 0000000..04715fd
--- /dev/null
+++ b/librecrypt_post_concat_adjust_.c
@@ -0,0 +1,25 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+extern inline int librecrypt_post_concat_adjust_(struct concat_state *state, size_t len);
+
+
+#else
+
+
+int
+main(void)
+{
+ SET_UP_ALARM();
+ INIT_RESOURCE_TEST();
+
+ /* TODO test */
+
+ STOP_RESOURCE_TEST();
+ return 0;
+}
+
+
+#endif
diff --git a/librecrypt_raw_len_to_base64_len_.c b/librecrypt_raw_len_to_base64_len_.c
new file mode 100644
index 0000000..c221b90
--- /dev/null
+++ b/librecrypt_raw_len_to_base64_len_.c
@@ -0,0 +1,72 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+extern inline int librecrypt_raw_len_to_base64_len_(size_t raw_len, int padded, size_t *ascii_len_out);
+
+
+#else
+
+
+#define CHECK(IN, PADDED, OUT)\
+ do {\
+ size_t out = SIZE_MAX;\
+ assert(librecrypt_raw_len_to_base64_len_((IN), (PADDED), &out) == 1);\
+ assert(out == (OUT));\
+ } while (0)
+
+
+int
+main(void)
+{
+ size_t i, in_off = 0u, out_off = 0u;
+
+ SET_UP_ALARM();
+ INIT_RESOURCE_TEST();
+
+ for (i = 0; i < 8u; i++) {
+ CHECK(in_off + 0u, 0, out_off + 0u);
+ CHECK(in_off + 1u, 0, out_off + 2u);
+ CHECK(in_off + 2u, 0, out_off + 3u);
+
+ CHECK(in_off + 0u, 1, out_off + 0u);
+ CHECK(in_off + 1u, 1, out_off + 4u);
+ CHECK(in_off + 2u, 1, out_off + 4u);
+
+ in_off += 3u;
+ out_off += 4u;
+ }
+
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u - 4u, 0, &(size_t){0}) == 1);
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u - 3u, 0, &(size_t){0}) == 1);
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u - 2u, 0, &(size_t){0}) == 1);
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u - 1u, 0, &(size_t){0}) == 1);
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u + 0u, 0, &(size_t){0}) == 1);
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u + 1u, 0, &(size_t){0}) == 1);
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u + 2u, 0, &(size_t){0}) == 1);
+
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u + 3u, 0, &(size_t){0}) == 0);
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u + 4u, 0, &(size_t){0}) == 0);
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u + 5u, 0, &(size_t){0}) == 0);
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u + 6u, 0, &(size_t){0}) == 0);
+
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u - 4u, 1, &(size_t){0}) == 1);
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u - 3u, 1, &(size_t){0}) == 1);
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u - 2u, 1, &(size_t){0}) == 1);
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u - 1u, 1, &(size_t){0}) == 1);
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u + 0u, 1, &(size_t){0}) == 1);
+
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u + 1u, 1, &(size_t){0}) == 0);
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u + 2u, 1, &(size_t){0}) == 0);
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u + 3u, 1, &(size_t){0}) == 0);
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u + 4u, 1, &(size_t){0}) == 0);
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u + 5u, 1, &(size_t){0}) == 0);
+ assert(librecrypt_raw_len_to_base64_len_(SIZE_MAX / 4u * 3u + 6u, 1, &(size_t){0}) == 0);
+
+ STOP_RESOURCE_TEST();
+ return 0;
+}
+
+
+#endif