/* See LICENSE file for copyright and license details. */
#include "common.h"
#ifdef __linux__
#include <sys/auxv.h>
#include <sys/random.h>
#include <stdint.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;
unsigned seed;
double x;
size_t i;
int xi;
#ifdef __linux__
ssize_t r;
# ifdef AT_RANDOM
uintptr_t raddr_;
uint16_t *raddr;
# endif
#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 (i < n) {
if (!srand_called) {
seed = (unsigned)time(NULL) ^ (unsigned)rand();
seed ^= (unsigned)(uintptr_t)out;
#if defined(__linux__) && defined(AT_RANDOM)
raddr_ = (uintptr_t)getauxval(AT_RANDOM);
if (raddr_) {
raddr = (void *)raddr_;
seed ^= (unsigned)raddr[0];
seed ^= (unsigned)raddr[1];
seed ^= (unsigned)raddr[2];
seed ^= (unsigned)raddr[3];
seed ^= (unsigned)raddr[4];
seed ^= (unsigned)raddr[5];
seed ^= (unsigned)raddr[6];
seed ^= (unsigned)raddr[7];
}
#endif
srand(seed);
srand_called = 1;
}
do {
xi = rand();
x = (double)xi;
x /= (double)RAND_MAX;
x *= 63;
out[i] = (char)x;
} while (++i < n);
}
}
for (i = 0; i < n; i++)
out[i] = ALPHABET[out[i] & 63];
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;
}