diff options
| author | Mattias Andrée <m@maandree.se> | 2026-05-08 22:29:35 +0200 |
|---|---|---|
| committer | Mattias Andrée <m@maandree.se> | 2026-05-08 22:29:35 +0200 |
| commit | 2d3a573977417d917c16742d8d9d8ead047d0ebc (patch) | |
| tree | caeac52856a9df0478e2bee53e5dda1f84422461 /argon2 | |
| parent | Add DEFAULT_SUPPORT option and improve DEPENDENCIES (diff) | |
| download | librecrypt-2d3a573977417d917c16742d8d9d8ead047d0ebc.tar.gz librecrypt-2d3a573977417d917c16742d8d9d8ead047d0ebc.tar.bz2 librecrypt-2d3a573977417d917c16742d8d9d8ead047d0ebc.tar.xz | |
Misc
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to '')
| -rw-r--r-- | argon2/hash.c | 123 | ||||
| -rw-r--r-- | argon2/is_algorithm.c | 2 | ||||
| -rw-r--r-- | argon2/make_settings.c | 102 | ||||
| -rw-r--r-- | argon2/test_supported.c | 19 |
4 files changed, 218 insertions, 28 deletions
diff --git a/argon2/hash.c b/argon2/hash.c index cf83907..5709df0 100644 --- a/argon2/hash.c +++ b/argon2/hash.c @@ -6,19 +6,126 @@ #include <libar2simplified.h> +#define RANGE(MIN, MAX) (uintmax_t)(MIN), (uintmax_t)(MAX) +#define BASE64 librecrypt_common_rfc4848s4_decoding_lut_, argon2__PAD, argon2__STRICT_PAD +#define REMOVE_CONST(X) (*(void **)(void *)&(X)) + + int librecrypt__argon2__hash(char *restrict out_buffer, size_t size, const char *phrase, size_t len, const char *settings, size_t prefix, void *reserved) { - /* TODO implement */ - (void) out_buffer; - (void) size; - (void) phrase; - (void) len; - (void) settings; - (void) prefix; + struct libar2_argon2_parameters params; + struct libar2_context ctx; + const char *type, *version, *salt_encoded; + uintmax_t mcost, tcost, lanes, saltlen, hashlen; + void *salt = NULL, *scratch = NULL; + size_t scratch_size; + ssize_t r; + int saved_errno; + + /* Not yet used */ (void) reserved; + + /* Gives us memory allocation and threading support; + * so we don't have to implement any of that ourselves */ + libar2simplified_init_context(&ctx); + /* Configure automatic erasure of input memory */ + ctx.autoerase_message = 0; /* allows `phrase` to be read-only */ + ctx.autoerase_secret = 0; /* alloes to params.key, which we are not using, but maybe in the future */ + ctx.autoerase_associated_data = 0; /* alloes to params.ad, which we are not using, but maybe in the future */ + ctx.autoerase_salt = 1; /* since we are decoding the salt, we do a memory allocation, + * and our testing always checks that allocated memory is earse; + * it doesn't really matter, but it's paranoid, and that's good */ + + /* Parse `settings` */ + r = librecrypt_check_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, + &mcost, RANGE(LIBAR2_MIN_M_COST, LIBAR2_MAX_M_COST), + &tcost, RANGE(LIBAR2_MIN_T_COST, LIBAR2_MAX_T_COST), + &lanes, RANGE(LIBAR2_MIN_LANES, LIBAR2_MAX_LANES), + &salt_encoded, &saltlen, RANGE(LIBAR2_MIN_SALTLEN, LIBAR2_MAX_SALTLEN), BASE64, + &hashlen, RANGE(LIBAR2_MIN_HASHLEN, LIBAR2_MAX_HASHLEN), BASE64); + if (!r) { + errno = EINVAL; + return -1; + } + + /* Decode salt */ + if (!salt_encoded) /* this would be if asterisk-notation is used, but it is not */ + abort(); + r = librecrypt_decode(NULL, 0u, salt_encoded, saltlen, BASE64); + if (r < 0) + return -1; + if (r > 0) { + /* We allow `r` to be 0, although that means saltlen is 0, + * which it cannot actaully be since LIBAR2_MIN_SALTLEN is 8, + * but who knows the future. Of course, we cannot run this + * part if `r` is 0, because we don't want to run malloc(3) + * with 0 because our test's implementation of malloc(3) + * doesn't allow that because it's implementation-defined, + * and we still would have to have a special case handling + * for implementations where it returns NULL, so instead + * we just let `salt` remain NULL, and `saltlen` remain 0. */ + salt = malloc((size_t)r); + if (!salt) + return -1; + if (librecrypt_decode(salt, (size_t)r, salt_encoded, saltlen, BASE64) != r) + abort(); + saltlen = (uintmax_t)r; + } + + /* Apply `settings` */ + params.type = type[1u] == 'd' ? LIBAR2_ARGON2ID : + type[1u] == 's' ? LIBAR2_ARGON2DS : + type[0u] == 'i' ? LIBAR2_ARGON2I : + LIBAR2_ARGON2ID; + params.version = version[3u] == '9' ? LIBAR2_ARGON2_VERSION_13 : /* 19 = 0x13 = 1.3 */ + LIBAR2_ARGON2_VERSION_10; /* 16 = 0x10 = 1.0 */ + params.t_cost = (uint_least32_t)tcost; + params.m_cost = (uint_least32_t)mcost; + params.lanes = (uint_least32_t)lanes; + params.salt = salt; + params.saltlen = (size_t)saltlen; + params.key = NULL; + params.keylen = 0u; + params.ad = NULL; + params.adlen = 0u; + params.hashlen = hashlen ? (size_t)hashlen : argon2__HASH_SIZE; + + /* Argon2 may require a larger buffer to work with for the hash than it outputs */ + scratch_size = libar2_hash_buf_size(¶ms); + if (scratch_size > size) { + scratch = malloc(scratch_size); + if (!scratch) + goto fail; + } + + /* Calculate hash */ + if (libar2_hash(scratch ? scratch : out_buffer, REMOVE_CONST(phrase), len, ¶ms, &ctx)) + goto fail; + + /* same rationale as for `ctx.autoerase_salt = 1;` */ + if (scratch) { + librecrypt_wipe(scratch, scratch_size); + free(scratch); + } else if (scratch_size > params.hashlen) { + librecrypt_wipe(&out_buffer[params.hashlen], scratch_size - params.hashlen); + } + + free(salt); return 0; + +fail: + saved_errno = errno; + if (salt) { + librecrypt_wipe(salt, saltlen); + free(salt); + } + errno = saved_errno; + return -1; } @@ -29,7 +136,9 @@ CONST int main(void) { SET_UP_ALARM(); + INIT_RESOURCE_TEST(); + STOP_RESOURCE_TEST(); return 0; } diff --git a/argon2/is_algorithm.c b/argon2/is_algorithm.c index a725f44..b13c415 100644 --- a/argon2/is_algorithm.c +++ b/argon2/is_algorithm.c @@ -27,6 +27,7 @@ int main(void) { SET_UP_ALARM(); + INIT_RESOURCE_TEST(); #if defined(SUPPORT_ARGON2I) CHECK(argon2i, "", "", 0u); @@ -76,6 +77,7 @@ main(void) CHECK(argon2ds, "$argon2id$", "", 0u); #endif + STOP_RESOURCE_TEST(); return 0; } diff --git a/argon2/make_settings.c b/argon2/make_settings.c index 94ee5ae..66ac3d6 100644 --- a/argon2/make_settings.c +++ b/argon2/make_settings.c @@ -2,11 +2,17 @@ #include "../common.h" #ifndef TEST +#include <libar2.h> + static 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) { + const char *p, *version = "19"; + size_t algolen, ret, min, len, i; + int r; + /* Use default RNG if NULL is specified */ if (!rng) rng = &librecrypt_rng_; @@ -17,22 +23,90 @@ make_settings(char *out_buffer, size_t size, const char *algorithm, size_t memco memcost = (size_t)4096u; /* 4 MiB */ } else { /* Function takes bytes as memcost, algorithm takes kilobytes */ - int memcost_round_up = memcost % 1024u >= 512u; + size_t memcost_round_up = (memcost >> 9) & 1u; memcost >>= 10; - memcost += memcost_round_up ? 1u : 0u; - memcost = memcost ? memcost : 1u; + memcost += memcost_round_up; } + memcost = memcost < LIBAR2_MIN_M_COST ? LIBAR2_MIN_M_COST : memcost; + memcost = memcost > LIBAR2_MAX_M_COST ? LIBAR2_MAX_M_COST : memcost; - /* TODO implement */ - (void) out_buffer; - (void) size; - (void) algorithm; - (void) memcost; - (void) timecost; - (void) gensalt; - (void) rng; - (void) user; - return 0; + /* Adjust `timecost` for algorithm */ + if (!timecost) { + /* Use default timecost if 0 was specified: + * default m_cost (4096) multiple by default t_cost (10) */ + timecost = 40960u; + } + timecost /= memcost; + timecost = timecost < LIBAR2_MIN_T_COST ? LIBAR2_MIN_T_COST : timecost; + timecost = timecost > LIBAR2_MAX_T_COST ? LIBAR2_MAX_T_COST : timecost; + + /* Get version */ + p = algorithm; + if (*p++ != '$') + abort(); + p = strchr(p, '$'); + algolen = p ? (size_t)(p - algorithm) : strlen(algorithm); + if (algolen > 64) /* just some small value absolute will fit all variants */ + abort(); + if (p++ && *p++ == 'v') { + if (!strncmp(p, "=16", 3u) && (!p[3u] || p[3u] == '$')) + version = "16"; + else if (!strncmp(p, "=19", 3u) && (!p[3u] || p[3u] == '$')) + version = "19"; + else + goto enosys; + } + + /* Write algorithm and parameters */ + r = snprintf(out_buffer, size, "%.*s$v=%s$m=%zu,t=%llu,p=1$", + (int)algolen, algorithm, version, + memcost, (unsigned long long int)timecost); + if (r < (int)sizeof("$argon2_$v=__$m=_,t=_,p=1$") - 1) + abort(); + ret = (size_t)r; + min = size ? ret < size - 1u ? ret : size - 1u : 0u; + out_buffer = &out_buffer[min]; + size -= min; + + /* Add 16 bytes of salt */ + if (gensalt) { + /* 16 bytes is 128 bits, and 128 = 21*6+2, so that is + * 21 full base-64 characeters and 1 that only use 2 bits */ + ret += len = 22u; + min = size ? len < size - 1u ? len : size - 1u : 0u; + if (librecrypt_fill_with_random_(out_buffer, min, rng, user)) + return -1; + if (min == len) + out_buffer[len - 1u] = (char)((unsigned char)out_buffer[len - 1u] & ~15u); + for (i = 0u; i < min; i++) + out_buffer[i] = librecrypt_common_rfc4848s4_encoding_lut_[((unsigned char *)out_buffer)[i]]; + } else { + ret += len = sizeof("*16") - 1u; + min = size ? len < size - 1u ? len : size - 1u : 0u; + memcpy(out_buffer, "*16", min); + } + out_buffer = &out_buffer[min]; + size -= min; + + /* Add tag size (size of hash result) */ + ret += len = sizeof("$*32") - 1u; + min = size ? len < size - 1u ? len : size - 1u : 0u; + memcpy(out_buffer, "$*32", min); + out_buffer = &out_buffer[min]; + size -= min; + + /* NUL terminate */ + if (size) { + /* We were careful to make sure size is positive at + * the end if it was when the function was called */ + *out_buffer = '\0'; + } + + return (ssize_t)ret; + +enosys: + errno = ENOSYS; + return -1; } @@ -59,7 +133,9 @@ CONST int main(void) { SET_UP_ALARM(); + INIT_RESOURCE_TEST(); + STOP_RESOURCE_TEST(); return 0; } diff --git a/argon2/test_supported.c b/argon2/test_supported.c index 4c40ee6..d2eba9a 100644 --- a/argon2/test_supported.c +++ b/argon2/test_supported.c @@ -5,19 +5,20 @@ #include <libar2.h> +#define RANGE(MIN, MAX) (uintmax_t)(MIN), (uintmax_t)(MAX) +#define BASE64 librecrypt_common_rfc4848s4_decoding_lut_, argon2__PAD, argon2__STRICT_PAD + + int librecrypt__argon2__test_supported(const char *phrase, size_t len, int text, const char *settings, size_t prefix, size_t *len_out) { - uintmax_t hashsize; + uintmax_t hashlen; int r; /* We don't care about password content, arbitrary binary is supported */ (void) phrase; (void) text; -#define RANGE(MIN, MAX) (uintmax_t)(MIN), (uintmax_t)(MAX) -#define BASE64 librecrypt_common_rfc4848s4_decoding_lut_, argon2__PAD, argon2__STRICT_PAD - /* Validate string format and parameters */ r = librecrypt_check_settings_(settings, prefix, "$%*$%sm=%p,t=%p,p=%p$%b$%^h", @@ -26,14 +27,14 @@ librecrypt__argon2__test_supported(const char *phrase, size_t len, int text, con RANGE(LIBAR2_MIN_T_COST, LIBAR2_MAX_T_COST), RANGE(LIBAR2_MIN_LANES, LIBAR2_MAX_LANES), RANGE(LIBAR2_MIN_SALTLEN, LIBAR2_MAX_SALTLEN), BASE64, - &hashsize, RANGE(LIBAR2_MIN_HASHLEN, LIBAR2_MAX_HASHLEN), BASE64); + &hashlen, RANGE(LIBAR2_MIN_HASHLEN, LIBAR2_MAX_HASHLEN), BASE64); if (!r) return 0; /* Return hash size */ - if (!hashsize) - hashsize = argon2__HASH_SIZE; - *len_out = (size_t)hashsize; + if (!hashlen) + hashlen = argon2__HASH_SIZE; + *len_out = (size_t)hashlen; /* Check password size */ #if SIZE_MAX > UINT32_MAX @@ -52,7 +53,9 @@ CONST int main(void) { SET_UP_ALARM(); + INIT_RESOURCE_TEST(); + STOP_RESOURCE_TEST(); return 0; } |
