aboutsummaryrefslogtreecommitdiffstats
path: root/argon2
diff options
context:
space:
mode:
Diffstat (limited to 'argon2')
-rw-r--r--argon2/hash.c123
-rw-r--r--argon2/is_algorithm.c2
-rw-r--r--argon2/make_settings.c102
-rw-r--r--argon2/test_supported.c19
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(&params);
+ 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, &params, &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;
}