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