aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--libar2simplified.71
-rw-r--r--libar2simplified.h46
-rw-r--r--libar2simplified_decode.31
-rw-r--r--libar2simplified_decode.c188
-rw-r--r--libar2simplified_decode_r.3148
-rw-r--r--libar2simplified_decode_r.c196
-rw-r--r--test.c6
8 files changed, 409 insertions, 178 deletions
diff --git a/Makefile b/Makefile
index 6827d97..544886c 100644
--- a/Makefile
+++ b/Makefile
@@ -19,6 +19,7 @@ LIB_NAME = ar2simplified
OBJ =\
libar2simplified_crypt.o\
libar2simplified_decode.o\
+ libar2simplified_decode_r.o\
libar2simplified_encode.o\
libar2simplified_encode_hash.o\
libar2simplified_hash.o\
diff --git a/libar2simplified.7 b/libar2simplified.7
index fa9219d..809ba1e 100644
--- a/libar2simplified.7
+++ b/libar2simplified.7
@@ -43,6 +43,7 @@ the parameters.
.BR libar2simplified (7),
.BR libar2simplified_crypt (3),
.BR libar2simplified_decode (3),
+.BR libar2simplified_decode_r (3),
.BR libar2simplified_encode (3),
.BR libar2simplified_encode_hash (3),
.BR libar2simplified_hash (3),
diff --git a/libar2simplified.h b/libar2simplified.h
index 0d005fb..e58e68c 100644
--- a/libar2simplified.h
+++ b/libar2simplified.h
@@ -105,6 +105,52 @@ struct libar2_argon2_parameters *
libar2simplified_decode(const char *str, char **tagp, char **endp, int (*random_byte_generator)(char *out, size_t n));
/**
+ * Decode hashing parameters
+ *
+ * If the salt's lengths is encoded, but not an
+ * actual salt, a random salt will be created
+ *
+ * The hashing string does not encode information
+ * about `params->key` or `params->ad`, therefore
+ * `params->key` and `params->ad` will be set to
+ * `NULL` and `params->keylen` and `params->adlen`
+ * will be set to 0; where `params` is the returned
+ * pointer
+ *
+ * @param str The hashing parameter string to decode
+ * @param tagp Output parameter for the tag (hash result), or `NULL`.
+ * Unless `NULL`, `NULL` will be stored in `*tagp` if `str`
+ * includes a tag length instead of an actual tag, otherwise
+ * unless `NULL`, the beginning of the tag, in `str`, will
+ * be stored in `*tagp`. `*endp` will (unless `endp` or
+ * `*tagp` is `NULL`) mark the end of the tag.
+ * @param endp Output parameter for the end of the hashing parameter
+ * string, or `NULL`. Unless `NULL`, one position beyond the
+ * last byte in `str` determined to be part of the hashing
+ * parameter string will be stored in `*endp`. The application
+ * shall make sure that `**endp` is a valid termination of
+ * the hashing parameter string; typically this would be a ':'
+ * or a NUL byte.
+ * @param random_byte_generator Random number generator function, used to generate salt if
+ * `str` does not contain one. The function shall output `n`
+ * random bytes (only the lower 6 bits in each byte need to
+ * be random) to `out` and return 0. On failure, the function
+ * shall return -1. If `NULL`, the function will use a random
+ * number generator provided by the C standard library or the
+ * operating system.
+ * @param user_data Will be parsed as is as the third argument of
+ * `random_byte_generator` and is otherwise unused.
+ * @return Decoded hashing parameters. Shall be deallocated using
+ * free(3) when no longer needed. Be aware than the allocation
+ * size of the returned object will exceed the size of the
+ * return type.
+ */
+LIBAR2_PUBLIC__ LIBAR2_NONNULL__(1)
+struct libar2_argon2_parameters *
+libar2simplified_decode_r(const char *str, char **tagp, char **endp,
+ int (*random_byte_generator)(char *out, size_t n, void *user_data), void *user_data);
+
+/**
* Calculate a password hash
*
* @param hash Output parameter for the tag (hash result).
diff --git a/libar2simplified_decode.3 b/libar2simplified_decode.3
index 8de6ddf..4a54ddc 100644
--- a/libar2simplified_decode.3
+++ b/libar2simplified_decode.3
@@ -133,6 +133,7 @@ deallocated.
.SH SEE ALSO
.BR libar2simplified (7),
+.BR libar2simplified_decode_r (3),
.BR libar2simplified_encode (3),
.BR libar2simplified_encode_hash (3),
.BR libar2simplified_recommendation (3),
diff --git a/libar2simplified_decode.c b/libar2simplified_decode.c
index 2c2f38e..eb3d3ee 100644
--- a/libar2simplified_decode.c
+++ b/libar2simplified_decode.c
@@ -1,194 +1,28 @@
/* See LICENSE file for copyright and license details. */
#include "common.h"
-#ifdef __linux__
-#include <sys/random.h>
-#endif
-#include <time.h>
-static size_t
-decode_u32(const char *s, uint_least32_t *outp)
-{
- uint_least32_t digit;
- size_t i;
-
- if ((s[0] == '0' && s[1] == '0') || !isdigit(s[0])) {
- errno = EINVAL;
- return 0;
- }
-
- *outp = 0;
- for (i = 0; isdigit(s[i]); i++) {
- digit = (uint_least32_t)(s[i] & 15);
- if (*outp > ((uint_least32_t)0xFFFFffffUL - digit) / 10) {
- errno = ERANGE;
- return 0;
- }
- *outp = *outp * 10 + digit;
- }
-
- return i;
-}
+union function {
+ int (*func)(char *out, size_t n);
+};
static int
-random_salt(char *out, size_t n, int (*random_byte_generator)(char *out, size_t n))
-{
-#define ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
- static int srand_called = 0;
-
- double x;
- size_t i;
- int xi;
-#ifdef __linux__
- ssize_t r;
-#endif
-
- if (random_byte_generator) {
- if (random_byte_generator(out, n))
- return -1;
- } else {
- i = 0;
-#ifdef __linux__
- for(; i < n; i += (size_t)r) {
- r = getrandom(&out[i], n - i, GRND_NONBLOCK);
- if(r < 0)
- break;
- }
-#endif
- if (!srand_called) {
- srand((unsigned int)time(NULL) ^ (unsigned int)rand());
- srand_called = 1;
- }
- for(; i < n; i++) {
- xi = rand();
- x = (double)xi;
- x /= (double)RAND_MAX;
- x *= 63;
- out[i] = (char)x;
- }
- }
-
- for (i = 0; i < n; i++)
- out[i] = ALPHABET[out[i] % 64];
- return 0;
-}
-
-
-static void *
-allocate(size_t num, size_t size, size_t alignment, struct libar2_context *ctx)
-{
- (void) ctx;
- (void) alignment;
- return malloc(num * size);
-}
-
-
-static void
-deallocate(void *ptr, struct libar2_context *ctx)
+function_wrapper(char *out, size_t n, void *function)
{
- (void) ctx;
- free(ptr);
+ union function *func = function;
+ return func->func(out, n);
}
struct libar2_argon2_parameters *
libar2simplified_decode(const char *str, char **tagp, char **endp, int (*random_byte_generator)(char *out, size_t n))
{
- struct libar2_argon2_parameters params, *ret;
- const char *p = str;
- const char *end;
- char *str_free = NULL;
- char *buf = NULL;
- size_t n, saltsize, offset;
- uint_least32_t saltlen, hashlen;
- struct libar2_context ctx;
-
- if (*p != '$')
- goto einval;
- p = strchr(&p[1], '$');
- if (!p)
- goto einval;
- if (p[1] == 'v' && p[2] == '=') {
- p = strchr(&p[1], '$');
- if (!p)
- goto einval;
- }
- p = strchr(&p[1], '$');
- if (!p)
- goto einval;
- p++;
- end = strchr(p, '$');
- if (!end)
- goto einval;
-
- if (*p == '*') {
- n = decode_u32(&p[1], &saltlen);
- if (!n++)
- goto fail;
- if (&p[n] != end)
- goto einval;
- params.saltlen = (size_t)saltlen;
- saltsize = libar2_encode_base64(NULL, NULL, saltlen) - 1;
- offset = (size_t)(p - str);
- str_free = malloc(offset + saltsize + strlen(&p[n]) + 1);
- if (!str_free)
- goto enomem;
- memcpy(str_free, str, offset);
- if (random_salt(&str_free[offset], saltsize, random_byte_generator))
- goto fail;
- offset += saltsize;
- stpcpy(&str_free[offset], &p[n]);
- str = str_free;
- }
- end++;
-
- ctx.allocate = allocate;
- ctx.deallocate = deallocate;
-
- if (!libar2_decode_params(str, &params, &buf, &ctx))
- goto fail;
-
- if (*end == '*') {
- n = decode_u32(&end[1], &hashlen);
- if (!n++)
- goto fail;
- end = &end[n];
- params.hashlen = (size_t)hashlen;
- if (tagp)
- *tagp = NULL;
+ union function func;
+ if (random_byte_generator) {
+ func.func = random_byte_generator;
+ return libar2simplified_decode_r(str, tagp, endp, function_wrapper, &func);
} else {
- if (tagp)
- *tagp = *(void **)(void *)&end;
- end = &end[libar2_encode_base64(NULL, NULL, params.hashlen) - 1];
+ return libar2simplified_decode_r(str, tagp, endp, NULL, NULL);
}
-
- ret = malloc(sizeof(params) + params.saltlen);
- if (!ret)
- goto enomem;
- memcpy(ret, &params, sizeof(params));
- if (buf) {
- memcpy(&((char *)ret)[sizeof(params)], buf, params.saltlen);
- ret->salt = &((unsigned char *)ret)[sizeof(params)];
- deallocate(buf, &ctx);
- }
-
- if (endp)
- *endp = *(void **)(void *)&end;
-
- free(str_free);
- return ret;
-
-einval:
- errno = EINVAL;
- return NULL;
-
-fail:
- free(str_free);
- return NULL;
-
-enomem:
- free(str_free);
- errno = ENOMEM;
- return NULL;
}
diff --git a/libar2simplified_decode_r.3 b/libar2simplified_decode_r.3
new file mode 100644
index 0000000..0cc229b
--- /dev/null
+++ b/libar2simplified_decode_r.3
@@ -0,0 +1,148 @@
+.TH LIBAR2SIMPLIFIED_DECODE_R 3 LIBAR2SIMPLIFIED
+.SH NAME
+libar2simplified_decode_r - Decode hashing parameters
+
+.SH SYNOPSIS
+.nf
+#include <libar2simplified.h>
+
+struct libar2_argon2_parameters *
+libar2simplified_decode_r(const char *\fIstr\fP, char **\fItagp\fP, char **\fIendp\fP,
+ int (*\fIrandom_byte_generator\fP)(char *\fIout\fP, size_t \fIn\fP, void *\fIuser_data\fP),
+ void *\fIuser_data\fP);
+.fi
+.PP
+Link with
+.IR "-lar2simplified -lar2" .
+
+.SH DESCRIPTION
+The
+.BR libar2simplified_decode_r ()
+function a decode hashing parameter string provided
+in the
+.I str
+parameter.
+.PP
+This function supports the extended format described in
+.BR libar2simplified_encode (3).
+If the parameter string only specifies a salt length, and
+not an actual salt, one is generate using the function
+provided via the
+.I random_byte_generator
+parameter; or if
+.I random_byte_generator
+is
+.IR NULL ,
+a function built into the library itself. If the parameter
+string specifies a tag (hash result), a pointer to it
+is stored in
+.IR *tagp ,
+otherwise
+.I *tagp
+is set to
+.IR NULL .
+.RI ( *tagp
+is only set unless
+.I tagp
+is
+.IR NULL )
+.PP
+Unless
+.I endp
+is
+.IR NULL ,
+.I *endp
+will be set to the end of the parameter string, which
+terminates the tag. The application shall make sure
+that
+.I *endp
+is a proper termination of the parameter string,
+typically this would be a colon
+.RB ( : ),
+if read from
+.I /etc/shadow
+or a similar file, or a NUL byte. The
+.BR libar2simplified_decode_r ()
+function will
+.B not
+make this check even if
+.I endp
+is
+.IR NULL .
+.PP
+Unless
+.I random_byte_generator
+is
+.IR NULL ,
+it shall generate
+.I n
+random bytes and store them in
+.I out
+and return 0, or on failure -1.
+.I user_data
+will be passed as is as the third argument to
+.IR *random_byte_generator .
+Each byte need only have its 6 lower bits set
+randomly.
+.PP
+The hashing string does not encode information
+about the secret (pepper) or associated data,
+which will therefore be set to zero-length.
+.PP
+.I params
+may not be
+.IR NULL .
+
+.SH RETURN VALUES
+The
+.BR libar2simplified_decode_r ()
+function returns a dynamically allocated
+structure detailing the contents of
+.IR str ,
+which can be deallocated using the
+.BR free (3)
+function, upon successful completion.
+On error,
+.I NULL
+is returned and
+.I errno
+is set to describe the error.
+
+.SH ERRORS
+The
+.BR libar2simplified_decode_r ()
+function will fail if:
+.TP
+.B EINVAL
+The contents of
+.I str
+is invalid or unsupported.
+.TP
+.B ENOMEM
+Insufficient storage space is available.
+.PP
+The
+.BR libar2simplified_decode_r ()
+function will fail if the
+.I random_byte_generator
+fails, in which case it will not modify
+the value of
+.IR errno .
+
+.SH NOTES
+The returned objects allocation size will
+exceed the size of its type, so that the
+salt can be stored in it, and automatically
+deallocated when the returned pointer is
+deallocated.
+
+.SH SEE ALSO
+.BR libar2simplified (7),
+.BR libar2simplified_decode (3),
+.BR libar2simplified_encode (3),
+.BR libar2simplified_encode_hash (3),
+.BR libar2simplified_recommendation (3),
+.BR libar2simplified_hash (3),
+.BR libar2_decode_params (3),
+.BR libar2_validate_params (3),
+.BR libar2_hash (3)
diff --git a/libar2simplified_decode_r.c b/libar2simplified_decode_r.c
new file mode 100644
index 0000000..e49907c
--- /dev/null
+++ b/libar2simplified_decode_r.c
@@ -0,0 +1,196 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifdef __linux__
+#include <sys/random.h>
+#endif
+#include <time.h>
+
+
+static size_t
+decode_u32(const char *s, uint_least32_t *outp)
+{
+ uint_least32_t digit;
+ size_t i;
+
+ if ((s[0] == '0' && s[1] == '0') || !isdigit(s[0])) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ *outp = 0;
+ for (i = 0; isdigit(s[i]); i++) {
+ digit = (uint_least32_t)(s[i] & 15);
+ if (*outp > ((uint_least32_t)0xFFFFffffUL - digit) / 10) {
+ errno = ERANGE;
+ return 0;
+ }
+ *outp = *outp * 10 + digit;
+ }
+
+ return i;
+}
+
+
+static int
+random_salt(char *out, size_t n, int (*random_byte_generator)(char *out, size_t n, void *user_data), void *user_data)
+{
+#define ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+ static int srand_called = 0;
+
+ double x;
+ size_t i;
+ int xi;
+#ifdef __linux__
+ ssize_t r;
+#endif
+
+ if (random_byte_generator) {
+ if (random_byte_generator(out, n, user_data))
+ return -1;
+ } else {
+ i = 0;
+#ifdef __linux__
+ for(; i < n; i += (size_t)r) {
+ r = getrandom(&out[i], n - i, GRND_NONBLOCK);
+ if(r < 0)
+ break;
+ }
+#endif
+ if (!srand_called) {
+ srand((unsigned int)time(NULL) ^ (unsigned int)rand());
+ srand_called = 1;
+ }
+ for(; i < n; i++) {
+ xi = rand();
+ x = (double)xi;
+ x /= (double)RAND_MAX;
+ x *= 63;
+ out[i] = (char)x;
+ }
+ }
+
+ for (i = 0; i < n; i++)
+ out[i] = ALPHABET[out[i] % 64];
+ return 0;
+}
+
+
+static void *
+allocate(size_t num, size_t size, size_t alignment, struct libar2_context *ctx)
+{
+ (void) ctx;
+ (void) alignment;
+ return malloc(num * size);
+}
+
+
+static void
+deallocate(void *ptr, struct libar2_context *ctx)
+{
+ (void) ctx;
+ free(ptr);
+}
+
+
+struct libar2_argon2_parameters *
+libar2simplified_decode_r(const char *str, char **tagp, char **endp,
+ int (*random_byte_generator)(char *out, size_t n, void *user_data),
+ void *user_data)
+{
+ struct libar2_argon2_parameters params, *ret;
+ const char *p = str;
+ const char *end;
+ char *str_free = NULL;
+ char *buf = NULL;
+ size_t n, saltsize, offset;
+ uint_least32_t saltlen, hashlen;
+ struct libar2_context ctx;
+
+ if (*p != '$')
+ goto einval;
+ p = strchr(&p[1], '$');
+ if (!p)
+ goto einval;
+ if (p[1] == 'v' && p[2] == '=') {
+ p = strchr(&p[1], '$');
+ if (!p)
+ goto einval;
+ }
+ p = strchr(&p[1], '$');
+ if (!p)
+ goto einval;
+ p++;
+ end = strchr(p, '$');
+ if (!end)
+ goto einval;
+
+ if (*p == '*') {
+ n = decode_u32(&p[1], &saltlen);
+ if (!n++)
+ goto fail;
+ if (&p[n] != end)
+ goto einval;
+ params.saltlen = (size_t)saltlen;
+ saltsize = libar2_encode_base64(NULL, NULL, saltlen) - 1;
+ offset = (size_t)(p - str);
+ str_free = malloc(offset + saltsize + strlen(&p[n]) + 1);
+ if (!str_free)
+ goto enomem;
+ memcpy(str_free, str, offset);
+ if (random_salt(&str_free[offset], saltsize, random_byte_generator, user_data))
+ goto fail;
+ offset += saltsize;
+ stpcpy(&str_free[offset], &p[n]);
+ str = str_free;
+ }
+ end++;
+
+ ctx.allocate = allocate;
+ ctx.deallocate = deallocate;
+
+ if (!libar2_decode_params(str, &params, &buf, &ctx))
+ goto fail;
+
+ if (*end == '*') {
+ n = decode_u32(&end[1], &hashlen);
+ if (!n++)
+ goto fail;
+ end = &end[n];
+ params.hashlen = (size_t)hashlen;
+ if (tagp)
+ *tagp = NULL;
+ } else {
+ if (tagp)
+ *tagp = *(void **)(void *)&end;
+ end = &end[libar2_encode_base64(NULL, NULL, params.hashlen) - 1];
+ }
+
+ ret = malloc(sizeof(params) + params.saltlen);
+ if (!ret)
+ goto enomem;
+ memcpy(ret, &params, sizeof(params));
+ if (buf) {
+ memcpy(&((char *)ret)[sizeof(params)], buf, params.saltlen);
+ ret->salt = &((unsigned char *)ret)[sizeof(params)];
+ deallocate(buf, &ctx);
+ }
+
+ if (endp)
+ *endp = *(void **)(void *)&end;
+
+ free(str_free);
+ return ret;
+
+einval:
+ errno = EINVAL;
+ return NULL;
+
+fail:
+ free(str_free);
+ return NULL;
+
+enomem:
+ free(str_free);
+ errno = ENOMEM;
+ return NULL;
+}
diff --git a/test.c b/test.c
index a656b99..cd22e45 100644
--- a/test.c
+++ b/test.c
@@ -10,6 +10,8 @@
#define SALT_ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+#define TIME_RECOMMENDATIONS 0
+
#define MEM(S) S, sizeof(S) - 1
@@ -183,6 +185,7 @@ check_random_salt_generate(void)
}
+#if TIME_RECOMMENDATIONS
static void
time_hash(const char *params_str, const char *params_name, int lineno)
{
@@ -216,6 +219,7 @@ time_hash(const char *params_str, const char *params_name, int lineno)
from_lineno = 0;
}
+#endif
static int
@@ -291,7 +295,7 @@ main(void)
assert_streq(libar2simplified_recommendation(1), RECOMMENDATION_SIDE_CHANNEL_FREE_ENVIRONMENT);
#endif
-#if 0
+#if TIME_RECOMMENDATIONS
#define TIME_HASH(PARAMS) time_hash(PARAMS, #PARAMS, __LINE__)
TIME_HASH(libar2simplified_recommendation(0));
TIME_HASH(libar2simplified_recommendation(1));