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