aboutsummaryrefslogtreecommitdiffstats
path: root/librecrypt_realise_salts.c
diff options
context:
space:
mode:
authorMattias Andrée <m@maandree.se>2026-05-01 17:45:39 +0200
committerMattias Andrée <m@maandree.se>2026-05-01 17:45:39 +0200
commitadfa8e1265f6155d1a582baa9929af198bb5d4de (patch)
treee3cee62aa5a8768621cd294295f787b8cc54141b /librecrypt_realise_salts.c
parentAdd librecrypt.7 and README (diff)
downloadlibrecrypt-adfa8e1265f6155d1a582baa9929af198bb5d4de.tar.gz
librecrypt-adfa8e1265f6155d1a582baa9929af198bb5d4de.tar.bz2
librecrypt-adfa8e1265f6155d1a582baa9929af198bb5d4de.tar.xz
Misc
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to 'librecrypt_realise_salts.c')
-rw-r--r--librecrypt_realise_salts.c154
1 files changed, 88 insertions, 66 deletions
diff --git a/librecrypt_realise_salts.c b/librecrypt_realise_salts.c
index a4204a6..dada7f1 100644
--- a/librecrypt_realise_salts.c
+++ b/librecrypt_realise_salts.c
@@ -9,71 +9,58 @@ librecrypt_realise_salts(char *restrict out_buffer, size_t size, const char *set
{
const char *lut;
char pad;
- int strict_pad, has_asterisk, nul_term = 0;
- size_t i, j, n, min, ret = 0u;
+ int strict_pad, nul_term = 0;
+ size_t i, min, nasterisks, prefix, ret = 0u;
size_t count, digit, q, r, left, mid, right;
+ /* If we are doing output, it should be NUL-terminated */
if (size) {
nul_term = 1;
size -= 1u;
}
- for (;;) {
- for (; *settings == LIBRECRYPT_ALGORITHM_LINK_DELIMITER; settings++) {
- if (size) {
- *out_buffer++ = LIBRECRYPT_ALGORITHM_LINK_DELIMITER;
- size -= 1u;
- }
- ret += 1u;
- }
-
- if (!*settings)
- break;
-
- has_asterisk = 0;
- for (n = 0u; settings[n] && settings[n] != LIBRECRYPT_ALGORITHM_LINK_DELIMITER; n++)
- if (settings[n] == '*')
- has_asterisk = 1;
-
- if (!has_asterisk) {
- min = size < n ? size : n;
- memcpy(out_buffer, settings, min);
- out_buffer += min;
- settings = &settings[n];
- ret += n;
- continue;
- }
-
- if (*settings == '*') {
- errno = EINVAL;
- return -1;
+ /* For each chained algorithm */
+ while (*settings) {
+ /* Get the number of '*' that are not in the result
+ * (hash size specification), and also length of
+ * the algorithm configuration, so we can identify
+ * the algorithm next and get its binary data encoding
+ * format (just calling librecrypt_get_encoding on the
+ * entire string would get it for the last algorithm
+ * in the chain) */
+ nasterisks = 0u;
+ count = 0u;
+ for (prefix = 0u; settings[prefix]; prefix++) {
+ if (settings[prefix] == LIBRECRYPT_HASH_COMPOSITION_DELIMITER)
+ nasterisks = count;
+ else if (settings[prefix] == LIBRECRYPT_ALGORITHM_LINK_DELIMITER)
+ break;
+ else if (settings[prefix] == '*')
+ count += 1u;
}
- lut = librecrypt_get_encoding(settings, n, &pad, &strict_pad, 0);
+ /* Get binary data encoding format */
+ lut = librecrypt_get_encoding(settings, prefix, &pad, &strict_pad, 0);
if (!lut)
return -1;
pad = strict_pad ? pad : '\0';
- for (i = 0; i < n;) {
- if (settings[i] != '*') {
- if (size) {
- *out_buffer++ = settings[i];
- size -= 1u;
- }
- ret += 1u;
- i++;
- continue;
- }
+ /* Process each asterisk-encoded salt */
+ while (nasterisks--) {
+ /* Copy text before next '*' */
+ for (i = 0u; settings[i] != '*'; i++);
+ min = i < size ? i : size;
+ memcpy(out_buffer, settings, min);
+ size -= min;
+ settings = &settings[i];
+ ret += i;
- if (++i == n) {
- if (size) {
- *out_buffer++ = '*';
- size -= 1u;
- }
- ret += 1u;
- break;
- }
- if ('0' > settings[i] || settings[i] > '9') {
+ /* Skip past the '*' */
+ settings++;
+
+ /* If the '*' is not followed by an unsigned,
+ * decimal integer include it literally */
+ if ('0' > settings[1u] || settings[1u] > '9') {
if (size) {
*out_buffer++ = '*';
size -= 1u;
@@ -82,49 +69,84 @@ librecrypt_realise_salts(char *restrict out_buffer, size_t size, const char *set
continue;
}
- count = 0;
- for (; i < n && '0' <= settings[i] && settings[i] <= '9'; i++) {
- digit = (size_t)(settings[i] - '0');
+ /* Parse encoded integer */
+ count = 0u;
+ while ('0' <= *settings && *settings <= '9') {
+ digit = (size_t)(*settings++ - '0');
if (count > ((size_t)SSIZE_MAX - digit) / 10u)
goto erange;
count *= 10u;
count += digit;
}
+ /* Get number of random base-64 characters to generated,
+ * and the number of padding characters to append */
+ /* 1 byte (8 bits) requires 2 base-64 characters (12 bits, 4 bits extra),
+ * 2 bytes (16 bits) requires 3 base-64 characters (18 bits, 2 bits extra), and
+ * 3 bytes (25 bits) requires 4 base-64 characters (24 bits) exactly */
q = count / 3u;
r = count % 3u;
- mid = r ? r + 1u : 0u;
- right = (!r ? 0u : pad ? 4u : r + 1u);
+ /* Full alphabet */
+ left = q * 4u + r;
+ /* Partial alphabet (extra bits are encoded as 0) */
+ mid = r ? 1u : 0u;
+ /* Padding: r==0: no padding,
+ * r==1: 2 base-64 characters, so 2 padding characters,
+ * r==2: 3 base-64 characters, so 1 padding characters */
+ right = (r && pad) ? 3u - r : 0u;
+ /* Get total length */
if (q > ((size_t)SSIZE_MAX - right) / 4u)
goto erange;
- left = q * 4u;
ret += left + mid + right;
+ /* Make sure we don't write more random characters than
+ * we have room for; we add `mid` into `left` so we have
+ * one variable for all random characters */
left += mid;
if (left > size) {
- left = size;
- mid = 0;
+ left = 0u;
+ mid = 0u;
}
+
+ /* Write random characters */
if (librecrypt_fill_with_random_(out_buffer, left, rng, user))
return -1;
- for (j = 0; j < left; j++)
- out_buffer[j] = lut[((unsigned char *)out_buffer)[j]];
- if (mid)
- out_buffer[j] = lut[((unsigned char *)out_buffer)[j] & (r == 1u ? ~15u : ~3u)];
+ for (i = 0u; i < left; i++)
+ out_buffer[i] = lut[((unsigned char *)out_buffer)[i]];
+ if (mid) {
+ i = left - 1u;
+ out_buffer[i] = lut[((unsigned char *)out_buffer)[i] & (r == 1u ? ~15u : ~3u)];
+ }
out_buffer = &out_buffer[left];
size -= left;
- if (right > size)
- right = size;
- for (j = 0; j < right; j++)
+ /* Write padding charaters */
+ right = right < size ? right : size;
+ for (i = 0u; i < right; i++)
out_buffer[right] = pad;
out_buffer = &out_buffer[right];
size -= right;
}
+
+ /* Copy remainder of the algorithm configuration, and the '>' if intermediate */
+ for (i = 0u; settings[i];)
+ if (settings[i++] == LIBRECRYPT_ALGORITHM_LINK_DELIMITER)
+ break;
+ min = i < size ? i : size;
+ memcpy(out_buffer, settings, min);
+ size -= min;
+ settings = &settings[i];
+ ret += i;
}
+ /* NUL-terminate the output if we were doing output */
if (nul_term)
*out_buffer = '\0';
+
+ /* Return the number of written bytes (excluding NUL byte):
+ * the length of the new password hash string, but ensure
+ * the new salts where not so large that our return value
+ * is out of range */
if (ret > (size_t)SSIZE_MAX)
goto erange;
return (ssize_t)ret;