255 lines
5.5 KiB
C
255 lines
5.5 KiB
C
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <pwd.h>
|
|
#include <errno.h>
|
|
#include <argon2.h>
|
|
|
|
#include <err.h>
|
|
#include "crypt.h"
|
|
|
|
/* defaults pulled from run.c */
|
|
#define HASHLEN 32
|
|
#define T_COST_DEF 3
|
|
#define LOG_M_COST_DEF 12 /* 2^12 = 4 MiB */
|
|
#define LANES_DEF 1
|
|
#define THREADS_DEF 1
|
|
#define OUTLEN_DEF 32
|
|
#define MAX_PASS_LEN 128
|
|
|
|
#define ARGON2_CONTEXT_INITIALIZER \
|
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
|
|
T_COST_DEF, LOG_M_COST_DEF,\
|
|
LANES_DEF, THREADS_DEF, \
|
|
ARGON2_VERSION_NUMBER, 0, 0, ARGON2_DEFAULT_FLAGS}
|
|
|
|
#define ARGON2_ARGON2_STR "argon2"
|
|
#define ARGON2_ARGON2I_STR "argon2i"
|
|
#define ARGON2_ARGON2D_STR "argon2d"
|
|
#define ARGON2_ARGON2ID_STR "argon2id"
|
|
|
|
/* getnum also declared in pw_getsalt.c */
|
|
/* maybe move to util.h?? */
|
|
static int
|
|
getnum(const char *str, size_t *num)
|
|
{
|
|
char *ep;
|
|
unsigned long rv;
|
|
|
|
if (str == NULL) {
|
|
*num = 0;
|
|
return 0;
|
|
}
|
|
|
|
rv = strtoul(str, &ep, 0);
|
|
|
|
if (str == ep || *ep) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (errno == ERANGE && rv == ULONG_MAX)
|
|
return -1;
|
|
*num = (size_t)rv;
|
|
return 0;
|
|
}
|
|
|
|
/* process params to argon2 */
|
|
/* we don't force param order as input, */
|
|
/* but we do provide the expected order to argon2 api */
|
|
static int decode_option(argon2_context * ctx, argon2_type * atype, const char * option)
|
|
{
|
|
size_t tmp=0;
|
|
char * in = 0,*inp;
|
|
char * a=0;
|
|
char * p=0;
|
|
size_t sl;
|
|
int error=0;
|
|
|
|
in = (char *)strdup(option);
|
|
inp = in;
|
|
|
|
if (*inp == '$') inp++;
|
|
|
|
a = strsep(&inp, "$");
|
|
|
|
sl = strlen(a);
|
|
|
|
if (sl == strlen(ARGON2_ARGON2I_STR) &&
|
|
!(strcmp(ARGON2_ARGON2I_STR, a))) {
|
|
*atype=Argon2_i;
|
|
} else if (sl == strlen(ARGON2_ARGON2D_STR) &&
|
|
!(strcmp(ARGON2_ARGON2D_STR, a))) {
|
|
*atype=Argon2_d;
|
|
}
|
|
else if (sl == strlen(ARGON2_ARGON2ID_STR) &&
|
|
!(strcmp(ARGON2_ARGON2ID_STR, a))) {
|
|
*atype=Argon2_id;
|
|
} else { /* default to id, we assume simple mistake */
|
|
/* don't abandon yet */
|
|
*atype=Argon2_id;
|
|
}
|
|
|
|
a = strsep(&inp, "$");
|
|
|
|
if ((getnum(a, &tmp))<0) { /* on error, default to current */
|
|
/* should start thinking about aborting */
|
|
ctx->version = ARGON2_VERSION_NUMBER;
|
|
} else {
|
|
ctx->version = tmp;
|
|
}
|
|
|
|
a = strsep(&inp, "$");
|
|
|
|
/* parse labelled argon2 params */
|
|
/* m_cost (m)
|
|
* t_cost (t)
|
|
* threads (p)
|
|
*/
|
|
while ((p = strsep(&a, ","))) {
|
|
switch (*p) {
|
|
case 'm':
|
|
p += strlen("m=");
|
|
if ((getnum(p, &tmp)) < 0) {
|
|
--error;
|
|
} else {
|
|
ctx->m_cost = tmp;
|
|
}
|
|
break;
|
|
case 't':
|
|
p += strlen("t=");
|
|
if ((getnum(p, &tmp)) < 0) {
|
|
--error;
|
|
} else {
|
|
ctx->t_cost = tmp;
|
|
}
|
|
break;
|
|
case 'p':
|
|
p += strlen("p=");
|
|
if ((getnum(p, &tmp)) < 0) {
|
|
--error;
|
|
} else {
|
|
ctx->threads = tmp;
|
|
}
|
|
break;
|
|
default:
|
|
return -1;
|
|
|
|
}
|
|
}
|
|
|
|
a = strsep(&inp, "$");
|
|
|
|
snprintf((char *)ctx->salt,ctx->saltlen, "%s", a);
|
|
|
|
a = strsep(&inp, "$");
|
|
|
|
if (*a) {
|
|
snprintf((char *)ctx->pwd,ctx->pwdlen, "%s", a);
|
|
} else {
|
|
/* don't care if passwd hash is missing */
|
|
/* if missing, most likely coming from */
|
|
/* pwhash or similar */
|
|
}
|
|
|
|
/* free our token buffer */
|
|
free(in);
|
|
|
|
/* 0 on success, <0 otherwise */
|
|
return error;
|
|
}
|
|
|
|
char *
|
|
__crypt_argon2(const char *pw, const char * salt)
|
|
{
|
|
/* we use the libargon2 api to generate */
|
|
/* return code */
|
|
int rc=0;
|
|
/* output buffer */
|
|
char ebuf[32];
|
|
/* ptr into argon2 encoded buffer */
|
|
char * blkp=0;
|
|
/* argon2 variable, default to id */
|
|
argon2_type atype = Argon2_id;
|
|
/* default to current argon2 version */
|
|
int version=ARGON2_VERSION_NUMBER;
|
|
/* argon2 context to collect params */
|
|
argon2_context ctx = ARGON2_CONTEXT_INITIALIZER;
|
|
/* argon2 encoded buffer */
|
|
char encodebuf[256];
|
|
/* argon2 salt buffer */
|
|
char saltbuf[128];
|
|
/* argon2 pwd buffer */
|
|
char pwdbuf[128];
|
|
/* returned static buffer */
|
|
static char rbuf[512];
|
|
|
|
/* clear buffers */
|
|
memset(encodebuf, 0, sizeof(encodebuf));
|
|
memset(saltbuf, 0, sizeof(saltbuf));
|
|
memset(pwdbuf, 0, sizeof(pwdbuf));
|
|
memset(rbuf, 0, sizeof(rbuf));
|
|
|
|
/* we use static buffers to avoid allocation */
|
|
/* and easier cleanup */
|
|
ctx.out = (uint8_t *)ebuf;
|
|
ctx.outlen = sizeof(ebuf);
|
|
|
|
ctx.out = (uint8_t *)encodebuf;
|
|
ctx.outlen = sizeof(encodebuf);
|
|
|
|
ctx.salt = (uint8_t *)saltbuf;
|
|
ctx.saltlen = sizeof(saltbuf);
|
|
|
|
ctx.pwd= (uint8_t *)pwdbuf;
|
|
ctx.pwdlen = sizeof(pwdbuf);
|
|
|
|
/* decode salt string to argon2 params */
|
|
/* argon2 context for param collection */
|
|
rc = decode_option(&ctx, &atype, salt);
|
|
|
|
if (rc < 0) {
|
|
/* unable to parse input params */
|
|
return 0;
|
|
}
|
|
|
|
rc = argon2_hash(ctx.t_cost, ctx.m_cost,
|
|
ctx.threads, pw, strlen(pw), ctx.salt, strlen((char*)ctx.salt),
|
|
ebuf, sizeof(ebuf), encodebuf, sizeof(encodebuf), atype, ctx.version);
|
|
|
|
if (rc != ARGON2_OK) {
|
|
fprintf(stderr, "Failed: %s\n", argon2_error_message(rc));
|
|
return 0;
|
|
}
|
|
|
|
/* get encoded passwd */
|
|
if ((blkp = strrchr(encodebuf, '$')) == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
/* skip over '$' */
|
|
blkp++;
|
|
|
|
/* we don't use encoded here because it base64 encodes salt */
|
|
/* same encoding format as argon2 api, but with original salt */
|
|
snprintf(rbuf, sizeof(rbuf)-1, "$%s$v=%d$m=%d,t=%d,p=%d$%s$%s",
|
|
argon2_type2string(atype,0),
|
|
version,
|
|
ctx.m_cost,
|
|
ctx.t_cost,
|
|
ctx.threads,
|
|
ctx.salt,
|
|
blkp);
|
|
|
|
/* clear buffers */
|
|
memset(encodebuf, 0, sizeof(encodebuf));
|
|
memset(saltbuf, 0, sizeof(saltbuf));
|
|
memset(pwdbuf, 0, sizeof(pwdbuf));
|
|
|
|
/* return encoded str */
|
|
return rbuf;
|
|
}
|