Home | History | Annotate | Line # | Download | only in libcrypt
crypt-argon2.c revision 1.1
      1 #include <stdlib.h>
      2 #include <stdio.h>
      3 #include <unistd.h>
      4 #include <stdio.h>
      5 #include <string.h>
      6 #include <time.h>
      7 #include <pwd.h>
      8 #include <errno.h>
      9 #include <argon2.h>
     10 
     11 #include <err.h>
     12 #include "crypt.h"
     13 
     14 /* defaults pulled from run.c */
     15 #define HASHLEN		32
     16 #define T_COST_DEF 	3
     17 #define LOG_M_COST_DEF 	12 /* 2^12 = 4 MiB */
     18 #define LANES_DEF 	1
     19 #define THREADS_DEF 	1
     20 #define OUTLEN_DEF 	32
     21 #define MAX_PASS_LEN 	128
     22 
     23 #define ARGON2_CONTEXT_INITIALIZER	\
     24 	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
     25 	T_COST_DEF, LOG_M_COST_DEF,\
     26 	LANES_DEF, THREADS_DEF, \
     27 	ARGON2_VERSION_NUMBER, 0, 0, ARGON2_DEFAULT_FLAGS}
     28 
     29 #define ARGON2_ARGON2_STR	"argon2"
     30 #define ARGON2_ARGON2I_STR	"argon2i"
     31 #define ARGON2_ARGON2D_STR	"argon2d"
     32 #define ARGON2_ARGON2ID_STR	"argon2id"
     33 
     34 /* getnum also declared in pw_getsalt.c */
     35 /* maybe move to util.h?? */
     36 static int
     37 getnum(const char *str, size_t *num)
     38 {
     39         char *ep;
     40         unsigned long rv;
     41 
     42         if (str == NULL) {
     43                 *num = 0;
     44                 return 0;
     45         }
     46 
     47         rv = strtoul(str, &ep, 0);
     48 
     49         if (str == ep || *ep) {
     50                 errno = EINVAL;
     51                 return -1;
     52         }
     53 
     54         if (errno == ERANGE && rv == ULONG_MAX)
     55                 return -1;
     56         *num = (size_t)rv;
     57         return 0;
     58 }
     59 
     60 /* process params to argon2 */
     61 /* we don't force param order as input, */
     62 /* but we do provide the expected order to argon2 api */
     63 static int decode_option(argon2_context * ctx, argon2_type * atype, const char * option)
     64 {
     65 	size_t tmp=0;
     66         char * in = 0,*inp;;
     67         char * a=0;
     68         char * p=0;
     69 	size_t sl;
     70 	int    error=0;
     71 
     72         in = (char *)strdup(option);
     73 	inp = in;
     74 
     75 	if (*inp == '$') inp++;
     76 
     77 	a = strsep(&inp, "$");
     78 
     79 	sl = strlen(a);
     80 
     81 	if (sl == strlen(ARGON2_ARGON2I_STR) &&
     82 	   !(strcmp(ARGON2_ARGON2I_STR, a))) {
     83 		*atype=Argon2_i;
     84 	} else if (sl == strlen(ARGON2_ARGON2D_STR) &&
     85 	        !(strcmp(ARGON2_ARGON2D_STR, a))) {
     86 		*atype=Argon2_d;
     87 	}
     88 	else if (sl == strlen(ARGON2_ARGON2ID_STR) &&
     89 	        !(strcmp(ARGON2_ARGON2ID_STR, a))) {
     90 		*atype=Argon2_id;
     91 	} else { /* default to id, we assume simple mistake */
     92 		/* don't abandon yet */
     93 		*atype=Argon2_id;
     94 	}
     95 
     96 	a = strsep(&inp, "$");
     97 
     98 	if ((getnum(a, &tmp))<0) { /* on error, default to current */
     99 				/* should start thinking about aborting */
    100 		ctx->version = ARGON2_VERSION_NUMBER;
    101 	} else {
    102 		ctx->version = tmp;
    103 	}
    104 
    105 	a = strsep(&inp, "$");
    106 
    107 	/* parse labelled argon2 params */
    108 	/* m_cost (m)
    109 	 * t_cost (t)
    110 	 * threads (p)
    111 	 */
    112 	while ((p = strsep(&a, ","))) {
    113 		switch (*p) {
    114 			case 'm':
    115 				p += strlen("m=");
    116 				if ((getnum(p, &tmp)) < 0) {
    117 					--error;
    118 				} else {
    119 					ctx->m_cost = tmp;
    120 				}
    121 				break;
    122 			case 't':
    123 				p += strlen("t=");
    124 				if ((getnum(p, &tmp)) < 0) {
    125 					--error;
    126 				} else {
    127 					ctx->t_cost = tmp;
    128 				}
    129 				break;
    130 			case 'p':
    131 				p += strlen("p=");
    132 				if ((getnum(p, &tmp)) < 0) {
    133 					--error;
    134 				} else {
    135 					ctx->threads = tmp;
    136 				}
    137 				break;
    138 			default:
    139 				return -1;
    140 
    141 		}
    142 	}
    143 
    144 	a = strsep(&inp, "$");
    145 
    146 	snprintf((char *)ctx->salt,ctx->saltlen, "%s", a);
    147 
    148 	a = strsep(&inp, "$");
    149 
    150 	if (*a) {
    151 		snprintf((char *)ctx->pwd,ctx->pwdlen, "%s", a);
    152 	} else {
    153 		/* don't care if passwd hash is missing */
    154 		/* if missing, most likely coming from */
    155 		/* pwhash or similar */
    156 	}
    157 
    158 	/* free our token buffer */
    159         free(in);
    160 
    161 	/* 0 on success, <0 otherwise */
    162         return error;
    163 }
    164 
    165 char *
    166 __crypt_argon2(const char *pw, const char * salt)
    167 {
    168 	/* we use the libargon2 api to generate */
    169 	/* return code */
    170 	int rc=0;
    171 	/* output buffer */
    172 	char ebuf[32];
    173 	/* ptr into argon2 encoded buffer */
    174 	char * blkp=0;
    175 	/* argon2 variable, default to id */
    176 	argon2_type atype = Argon2_id;
    177 	/* default to current argon2 version */
    178 	int version=ARGON2_VERSION_NUMBER;
    179 	/* argon2 context to collect params */
    180 	argon2_context ctx = ARGON2_CONTEXT_INITIALIZER;
    181 	/* argon2 encoded buffer */
    182 	char encodebuf[256];
    183 	/* argon2 salt buffer */
    184 	char saltbuf[128];
    185 	/* argon2 pwd buffer */
    186 	char pwdbuf[128];
    187 	/* returned static buffer */
    188 	static char rbuf[512];
    189 
    190 	/* clear buffers */
    191 	memset(encodebuf, 0, sizeof(encodebuf));
    192 	memset(saltbuf, 0, sizeof(saltbuf));
    193 	memset(pwdbuf, 0, sizeof(pwdbuf));
    194 	memset(rbuf, 0, sizeof(rbuf));
    195 
    196 	/* we use static buffers to avoid allocation */
    197 	/* and easier cleanup */
    198 	ctx.out = (uint8_t *)ebuf;
    199 	ctx.outlen = sizeof(ebuf);
    200 
    201 	ctx.out = (uint8_t *)encodebuf;
    202 	ctx.outlen = sizeof(encodebuf);
    203 
    204 	ctx.salt = (uint8_t *)saltbuf;
    205 	ctx.saltlen = sizeof(saltbuf);
    206 
    207 	ctx.pwd= (uint8_t *)pwdbuf;
    208 	ctx.pwdlen = sizeof(pwdbuf);
    209 
    210 	/* decode salt string to argon2 params */
    211 	/* argon2 context for param collection */
    212 	rc = decode_option(&ctx, &atype, salt);
    213 
    214 	if (rc < 0) {
    215 	/* unable to parse input params */
    216 		return 0;
    217 	}
    218 
    219 	rc = argon2_hash(ctx.t_cost, ctx.m_cost,
    220 		ctx.threads, pw, strlen(pw), ctx.salt, strlen((char*)ctx.salt),
    221 		ebuf, sizeof(ebuf), encodebuf, sizeof(encodebuf), atype, ctx.version);
    222 
    223 	if (rc != ARGON2_OK) {
    224 		fprintf(stderr, "Failed: %s\n", argon2_error_message(rc));
    225 		return 0;
    226 	}
    227 
    228 	/* get encoded passwd */
    229 	if ((blkp = strrchr(encodebuf, '$')) == NULL) {
    230 		return 0;
    231 	}
    232 
    233 	/* skip over '$' */
    234 	blkp++;
    235 
    236 	/* we don't use encoded here because it base64 encodes salt */
    237 	/* same encoding format as argon2 api, but with original salt */
    238 	snprintf(rbuf, sizeof(rbuf)-1, "$%s$v=%d$m=%d,t=%d,p=%d$%s$%s",
    239 			argon2_type2string(atype,0),
    240 			version,
    241 			ctx.m_cost,
    242 			ctx.t_cost,
    243 			ctx.threads,
    244 			ctx.salt,
    245 			blkp);
    246 
    247 	/* clear buffers */
    248 	memset(encodebuf, 0, sizeof(encodebuf));
    249 	memset(saltbuf, 0, sizeof(saltbuf));
    250 	memset(pwdbuf, 0, sizeof(pwdbuf));
    251 
    252 	/* return encoded str */
    253 	return rbuf;
    254 }
    255