From 2d3a573977417d917c16742d8d9d8ead047d0ebc Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Fri, 8 May 2026 22:29:35 +0200 Subject: Misc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- argon2/hash.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 116 insertions(+), 7 deletions(-) (limited to 'argon2/hash.c') 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 +#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; } -- cgit v1.2.3-70-g09d2