From f74764136b8b0564aa0e75e5c667b9d11158a2a9 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Tue, 15 Feb 2022 16:21:36 +0100 Subject: Add libar2simplified_decode_r MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- Makefile | 1 + libar2simplified.7 | 1 + libar2simplified.h | 46 +++++++++++ libar2simplified_decode.3 | 1 + libar2simplified_decode.c | 188 +++--------------------------------------- libar2simplified_decode_r.3 | 148 +++++++++++++++++++++++++++++++++ libar2simplified_decode_r.c | 196 ++++++++++++++++++++++++++++++++++++++++++++ test.c | 6 +- 8 files changed, 409 insertions(+), 178 deletions(-) create mode 100644 libar2simplified_decode_r.3 create mode 100644 libar2simplified_decode_r.c 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 @@ -104,6 +104,52 @@ LIBAR2_PUBLIC__ LIBAR2_NONNULL__(1) 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 * 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 -#endif -#include -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, ¶ms, &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, ¶ms, 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 + +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 +#endif +#include + + +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, ¶ms, &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, ¶ms, 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)); -- cgit v1.2.3-70-g09d2